borland c – manual de utilizare · borland c – manual de utilizare ... int modul (int i) //...

108
BORLAND C – Manual de utilizare BORLAND C – Manual de utilizare LECŢIA 1. STRUCTURA GENERALĂ A UNUI PROGRAM C 1.1 ISTORIC, CONCEPŢIE, EVOLUŢIE Limbajul C a fost finalizat în 1972 de Dennis M. Ritchie si Brian W. Kernighan de la firma americană Bell Laboratories . Prima versiune a limbajului se numeşte BCPL apoi următoarele poartă numele de A, B şi C. Cei doi autori au dezvoltat aceste prime versiuni în jurul sistemului de operare UNIX. La vremea respectivă din aproximativ 13000 linii sursă ale UNIX-ului doar 200 de linii sursă nu erau scrise în limbajul C. De acest fapt se leagă detractorii limbajului care spun că limbajul C nu este un limbaj deosebit ci doar un fel de limbaj “oficial” al sistemului de operare UNIX. În anul 1978 apare manualul The C Programming Language care este de fapt şi prima standardizare a limbajului. Cei doi autori intră astfel în istorie... După anul 1980 odată cu dezvoltarea hardware apar şi primele PC-uri iar acestea implică şi produse software adecvate. Principalele firme producătoare de sofware -MICROSOFT şi BORLAND - au dezvoltat unelte adecvate pentru programarea şi utilizarea limbajului C. Deocamdată firma BORLAND deţine supremaãţia prin versiunile mediului BORLAND C. Cele mai folosite sunt versiunile 2.0, 3.1, 4.0. În ultimii doi ani au apărut aşa numitele medii “visuale”: VISUAL C versiunile 4.5 şi 5.0 care sunt de fapt extensii ale mediului BORLAND C adaptate programării orientate obiect şi interfeţei grafice WINDOWS 95. Mediile de programare BORLANDC pot compila 3 tipuri de programe sursă C: fişiere cu extensia .C (fişiere cu prg. standard C); fişiere cu extensia .CP (fişiere cu prg. C+,); fişiere cu extensia .CPP (fişiere cu programe C++). Menţionăm că limbajul C++ a fost elaborat de Bjarne Stroustrup de la AT&T. El este un superset al limbajului C şi permite principalele concepte ale programării prin abstractizarea datelor şi programării orientate spre obiecte. Limbajul C este un limbaj hibrid având facilităţi caracteristice limbajelor de asamblare cât şi facilităţi ale limbajelor de înalt nivel. Câteva dintre principalele caracteristici ale limbajului C sunt: portabilitate: chiar dacă acest concept nu-i definit foarte riguros spunem că un program este portabil dacă el poate fi transferat uşor de la un tip de calculator la altul; limbajul C este un astfel de limbaj; flexibilitate: compilatorul face un număr mai redus de controale (face multe conversii implicite); programare structurată: limbajul are principalele structuri ale programării structurate: structura secvenţială, structura iterativă şi structura de selecţie; compactizare: unele instrucţiuni sunt scrise foarte compact; de exemplu i:=i+1 se poate scrie mai scurt ca i++; lucrul pe biţi şi calcule cu adrese. 1.2 CONCEPTUL DE FUNCŢIE Un program C se compune din una sau mai multe funcţii. Funcţia este o unitate lexicală de program compilabilă independent. Una dintre funcţii este funcţie principală, numele ei este predefinit şi anume main. Execuţia programului începe cu prima instrucţiune din funcţia principală. Funcţia principală main este obligatorie pentru orice program celelalte elemente fiind optionale. Pentru ca o anumită funcţie să poată fi apelată e necesar ca ea să aibă declarat prototipul dacă implementarea (definiţia) ei se află după funcţia main (exemplul 1). Dacă funcţia principală se află la sfârşitul fişierului atunci nu mai e necesar prototipul funcţiei apelate ci doar implementarea ei (exemplul 2). Comparând structura unui program C cu structura unui program PASCAL se observă nivelul de imbricare diferit. În PASCAL apare o imbricare a procedurilor şi funcţiilor pe când în C nu există o astfel de imbricare (rămâne la nivelul fiecărei funcţii dacă e cazul). - 1 -

Upload: letruc

Post on 13-May-2019

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

BORLAND C – Manual de utilizareLECŢIA 1.

STRUCTURA GENERALĂ A UNUI PROGRAM C1.1 ISTORIC, CONCEPŢIE, EVOLUŢIELimbajul C a fost finalizat în 1972 de Dennis M. Ritchie si Brian W.

Kernighan de la firma americană Bell Laboratories. Prima versiune a limbajului se numeşte BCPL apoi următoarele poartă numele

de A, B şi C. Cei doi autori au dezvoltat aceste prime versiuni în jurul sistemului de operare UNIX.

La vremea respectivă din aproximativ 13000 linii sursă ale UNIX-ului doar 200 de linii sursă nu erau scrise în limbajul C. De acest fapt se leagă detractorii limbajului care spun că limbajul C nu este un limbaj deosebit ci doar un fel de limbaj “oficial” al sistemului de operare UNIX.

În anul 1978 apare manualul The C Programming Language care este de fapt şi prima standardizare a limbajului. Cei doi autori intră astfel în istorie...

După anul 1980 odată cu dezvoltarea hardware apar şi primele PC-uri iar acestea implică şi produse software adecvate. Principalele firme producătoare de sofware -MICROSOFT şi BORLAND - au dezvoltat unelte adecvate pentru programarea şi utilizarea limbajului C. Deocamdată firma BORLAND deţine supremaãţia prin versiunile mediului BORLAND C. Cele mai folosite sunt versiunile 2.0, 3.1, 4.0. În ultimii doi ani au apărut aşa numitele medii “visuale”: VISUAL C versiunile 4.5 şi 5.0 care sunt de fapt extensii ale mediului BORLAND C adaptate programării orientate obiect şi interfeţei grafice WINDOWS 95. Mediile de programare BORLANDC pot compila 3 tipuri de programe sursă C:

fişiere cu extensia .C (fişiere cu prg. standard C); fişiere cu extensia .CP (fişiere cu prg. C+,); fişiere cu extensia .CPP (fişiere cu programe C++).Menţionăm că limbajul C++ a fost elaborat de Bjarne Stroustrup de la AT&T.

El este un superset al limbajului C şi permite principalele concepte ale programării prin abstractizarea datelor şi programării orientate spre obiecte.

Limbajul C este un limbaj hibrid având facilităţi caracteristice limbajelor de asamblare cât şi facilităţi ale limbajelor de înalt nivel.

Câteva dintre principalele caracteristici ale limbajului C sunt:• portabilitate: chiar dacă acest concept nu-i definit foarte riguros spunem că

un program este portabil dacă el poate fi transferat uşor de la un tip de calculator la altul; limbajul C este un astfel de limbaj;

• flexibilitate: compilatorul face un număr mai redus de controale (face multe conversii implicite);

• programare structurată: limbajul are principalele structuri ale programării structurate: structura secvenţială, structura iterativă şi structura de selecţie;

• compactizare: unele instrucţiuni sunt scrise foarte compact; de exemplu i:=i+1 se poate scrie mai scurt ca i++;

• lucrul pe biţi şi calcule cu adrese.

1.2 CONCEPTUL DE FUNCŢIEUn program C se compune din una sau mai multe funcţii. Funcţia este o

unitate lexicală de program compilabilă independent.Una dintre funcţii este funcţie principală, numele ei este predefinit şi

anume main.Execuţia programului începe cu prima instrucţiune din funcţia principală.

Funcţia principală main este obligatorie pentru orice program celelalte elemente fiind optionale. Pentru ca o anumită funcţie să poată fi apelată e necesar ca ea să aibă declarat prototipul dacă implementarea (definiţia) ei se află după funcţia main (exemplul 1). Dacă funcţia principală se află la sfârşitul fişierului atunci nu mai e necesar prototipul funcţiei apelate ci doar implementarea ei (exemplul 2). Comparând structura unui program C cu structura unui program PASCAL se observă nivelul de imbricare diferit. În PASCAL apare o imbricare a procedurilor şi funcţiilor pe când în C nu există o astfel de imbricare (rămâne la nivelul fiecărei funcţii dacă e cazul).

- 1 -

Page 2: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

1.2.1. Definiţia unei funcţiiDefiniţia unei funcţii în limbajul C se compune din antet şi corp. O

funcţie poate fi apelată dacă este precedată de definiţia sau de prototipul ei. Aceste lucruri care sunt valabile în limbajul C se regăsesc şi în limbajul C++.

Antet şi prototipAntetul simplificat al unei funcţii în C are formatul:

tip nume_funcţie (lista_parametrilor_formali)unde: tip - reprezintă tipul valorii returnate de funcţie sau dacă funcţia

nu returnează nici o valoare se pune cuvântul cheie void;nume_funcţie - reprezintă un identificator clasic format dintr-un mixaj de litere şi cifre, primul caracter fiind obligatoriu litera; printre litere se numără şi liniuţa de subliniere;lista_parametrilor_formali – nume de variabile separate prin virgule. Exemple: 1) double radical (double x) // calculeaza radacina patrata din x si

returneaza valoarea gasita2) double radical_n (double x, int n) // calculeaza radacina de

ordinul n din x

Prototipul unei funcţii este asemănător antetului dar la sfârşit se pune caracterul “;”

1.2.3. Corpul unei funcţiiCorpul unei funcţii C se compune din declaraţii de variabile locale şi

instrucţiuni scrise între acolade conform figurii de mai jos. { declaraţii instrucţiuni } Şi pentru că autorii limbajului C consideră că un limbaj de programare se

învaţă mai repede scriind şi executând programe căt mai timpuriu vom da un mic exemplu de funcţie.

int modul (int i) // determina valoarea absoluta a intregului i si retruneaza aceasta valoare

{ if (i < 0) return –i; if (i = = 0) return 0; else return i;}

1.3. CONSTRUCŢIILE DE BAZĂ ALE LIMBAJULUI C1.3.1. Caractere (Alfabetul)Limbajul C foloseşte setul de caractere al codului ASCII care se codifică

prin numere întregi din intervalul [0,127], adică 128 de coduri. Un astfel de întreg se păstrează pe un BYTE (OCTET) adică pe 8 biţi.

Mulţimea caracterelor se împarte în trei grupe:- caractere negrafice (coduri cuprinse între 00=NUL şi 31) şi 127=DEL;- spaţiu (codul 32);- caractere grafice (coduri cuprinse între 33 şi 126).

Caracterele grafice se împart la rândul lor în:litere mari (coduri între 65 şi 90);litere mici (coduri între 97 şi 122);cifre (coduri între 48 şi 59);caractere speciale (celelalte coduri).

Caracterele negrafice au diferite funcţii. Astfel codul 10 semnifică LF (Line Feed), adică deplasează cursorul pe linia următoare în coloana 1, iar codul 13 semnifică CR (Carriage Return) adică deplasează cursorul în coloana 1 aceeaşi linie. În limbajul C combinaţia celor două caractere se notează prin: \n şi are semnificaţia trecerii cursorului la linie nouă şi coloana 1 (newline).

Tabulatorul orizontal (deplasarea cursorului peste un anumit număr de poziţii) se precizează prin notaţia: \t

Să mai precizăm că următoarele 3 caractere: spaţiu, newline şi tabulator orizontal se mai numesc spaţii albe (white spaces).

- 2 -

Page 3: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

1.3.2. Nume (Identificatori)În limbajul C, un nume este o succesiune de litere şi eventual cifre, care

începe cu o literă. Ca lungime un nume poate fi oricât de lung dar numai primele 32 de caractere se iau în considerare. Folosind notaţia BNF (vezi anexa) un nume se poate defini recursiv, ca mai jos:

<nume> ::= <litera> | <nume><litera> | <nume><cifra><litera> ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Z| a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|z|_<cifra> ::= 0|1|2|3|4|5|6|7|8|9|Identificatorii se folosesc pentru denumirea variabilelor, tablourilor,

funcţiilor, etc.Important Limbajul C/C++ este “case sensitive” adica un identificator

scris cu litere mari nu este echivalent cu acelasi identificator scris cu litere mici.

Exemple:A, _start, a_, matrice, matrice_patratica.

Dăm şi câteva contraxemple: &B - conţine caracterul &; x+y - conţine caracterul +;

1.3.3. Cuvinte cheieUn cuvânt cheie este un cuvânt împrumutat din limba engleză, care are un

înţeles predefinit. Aceste cuvinte se scriu cu litere mici. Un cuvânt cheie nu poate avea altă utilizare într-un program C decât cea care i-a fost predefinită. Fiind o succesiune de litere, un cuvânt cheie este un nume. Lista cuvintelor cheie se dă în anexa de la sfârşitul cărţii. Sensul fiecărui cuvânt cheie va rezulta la definirea construcţiei în care se utilizează.

Exemple:for, do, if, while, else, break, return, int, long, double, static,

extern.

1.3.4. Tipuri de bazăÎn limbajul C distingem câteva tipuri predefinte de date care se mai

numesc tipuri de bază. În general un tip de dată are trei atribute bine conturate:

mulţimea valorilor;reprezentarea (internă şi externă)comportamentul (adică operaţiile ce se pot face cu acel tip).În tabelul de mai jos dăm cuvântul cheie, mulţimea valorilor,

reprezentarea internă.Reprezentarea internă

Cuvânt cheie Mulţimea valorilor Lungime (biţi) Formatul intern

int întregii din [-215; 215-1] 16 reprezentare binară short întregii din [-215; 215-1] 16 reprezentare binară long întregii din [-231; 231 - 1] 32 reprezentare binară unsigned întregii din [0; 216-1] 16 reprezentare binară char 8 cod ASCII float [-3.4*10-38; 3.4*1038] 32 virgulă flot. simplă prec. double [-1.7*10-308; 1.7*10308] 64 virgulă flotantă dublă

precizie

Facem precizarea că se poate folosi şi combinaţia unsigned long pentru întregii fără semn în dublă precizie.

Cu tipurile de bază se pot defini şi tipuri utilizator folosind instrucţiunea struct.

1.3.5. ConstanteO constantă are un tip şi o valoare. Atât tipul cât şi valoarea unei

constante se definesc prin caracterele care compun constanta respectivă.

Constantă întreagă zecimalăO constantă întreagă este un şir de cifre care eventual este prefixat de

un semn (+ sau -) şi postfixat de litera L sau l. O constantă întreagă se - 3 -

Page 4: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

reprezintă în binar pe 16 sau 32 de biţi. Sufixul L sau l forţează reprezentarea pe 32 de biţi.

Constantă întreagă octală O constantă întreagă octală este un şir de cifre octale (cifre cuprinse

între 0 şi 7) precedat de un zero nesemnificativ.

Constantă întreagă hexazecimală O constantă întreagă hexazecimală este un şir de cifre hexazecimale

prefixat cu 0x sau 0X.Exemple: Reprezentare Interpretare12345 întreg zecimal reprezentat în binar pe 16 biţi-12345 întreg zecimal reprezentat în binar pe 16 biţi12345L întreg zecimal reprezentat în binar pe 32 biţi012345 înt. octal reprez. în binar pe 16 biţi (o cifră oct. pe 3 biţi)0xabcd întreg hexa reprezentat în binar pe 16 biţi (o cifră pe 4 biţi) 12345678 întreg zecimal reprezentat pe 32 de biţi

Constantă flotantăO constantă flotantă este un număr raţional care se compune din

următoarele elemente:un semn (+ sau -) care poate lipsi pentru numerele pozitive;o parte întreagă care poate fi şi vidă;o parte fracţionară care poate fi şi vidă;un exponent care poate fi şi vid.Prezenţa părţii fracţionare este suficientă pentru ca numărul respectiv să

reprezinte o constantă flotantă. Absenţa părţii fracţionare implică prezenţa părţii întregi şi a exponentului.

Partea întreagă reprezintă o succesiune de cifre zecimale. Partea fracţionară se compune din caracterul punct urmat de o succesiune de cifre zecimale, care poate fi şi vidă. Exponentul se compune din litera e sau E urmată de un + sau -, opţional, şi un şir de cifre zecimale.

Constantele flotante se păstrează în format flotant dublă precizie.Exemple:3.14; 0.314e1; 3.1415926; 2.71828; 2.; 271828E-5.

Constantă caracterO constantă caracter reprezintă un caracter şi are ca valoare codul ASCII

al caracterului respectiv. O constantă caracter grafic se poate scrie incluzând caracterul respectiv între caractere apostrof.

Exemple:

Constantă caracter valoare cod ASCII

‘A’ 65‘B’ 66 ‘a’ 97‘0’ 48‘9’ 58‘*’ 77

Anumite caractere negrafice au notaţii speciale la scrierea cărora se utilizează caracterul backslash (\). Dăm în continuare un tabel cu cele mai folosite caractere negrafice:

Caracter Reprezentare ca şi constantă caracter Valoare cod ASCIIBackspace ‘\b’ 8Retur de car ‘\r’ 13Newline ‘\n’ 10 Apostrof ‘\’’ 39Backslash ‘\\’ 92Tabulator vertical ‘\v’ 11Salt pagină imprimantă ‘\f’ 12Carcterul NUL ‘\0’ 0

- 4 -

Page 5: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Constantă şir de caractereO constantă şir de caractere este o succesiune de zero sau mai multe

caractere delimitate prin ghilimele. Ghilimelele nu fac parte din şirul de caractere.

Exemple:“123”; “Limbajul C”; “sir de caractere”; “sir\””; “”(şirul vid).Constanta şir de caractere se reprezintă în memoria calculatorului printr-

o succesiune de octeţi în care se păstrează codurile ASCII ale caracterelor şirului, iar ultimul octet conţine caracterul NUL care marchează sfârşitul şirului de caractere.

După cum se observă din exemplele date putem să folosim şi convenţia cu backslash. De reţinut că ‘X’ şi “X” reprezintă construcţii diferite.

1.3.6. Variabile simplePrin variabilă înţelegem o dată a cărei valoare se poate schimba în timpul

execuţiei programului care o conţine. Unei variabile i se ataşează un nume prin intermediul căruia o putem referi sau modifica. Totodată valorile pe care le poate lua o variabilă trebuie să aparţină aceluiaşi tip. Corespondenţa dintre numele şi tipul unei variabile se realizează printr-o construcţie specială numită declaraţie.

După modul de grupare datele sunt:date izolate denumite şi variabile simple;date grupate: - mulţimi ordonate de date de acelaşi tip la care ne referim cu indici, - structuri în care date de tipuri diferite se grupează.Declaraţia de variabilă simplă are formatul general:tip listă_de_nume;unde listă_de_nume este formată fie dintr-un singur nume fie din mai

multe, separate prin virgule.În limbajul C trebuie declarate toate variabilele înainte de a fi

utilizate.Exemple:int a,b,c; // a, b, c sunt variabile de tip int reprezentate in binar pe

2 octeţi; float x,y,z; // x, y, z sunt variabile de tip float in format flotant

simpla precizie pe 32 de biţi; char car; // car este o variabila de tip char pe 1 octeti poate conţine

coduri ASCII; long i,j,k; // i, j, k sunt variabile de tip long in format binar pe

32 de biţi.

TablouriUn tablou ca orice variabilă simplă trebuie declarat înainte de a fi

utilizat. Dacă referirea la elementele tabloului se face cu un singur indice se spune că tabloul este unidimensional (se mai numeşte vector); dacă referirea se face cu doi indici tabloul se numeşte bidimensional(matrice); iar cu n indici tabloul este n-dimensional.

Declaraţia unui tablou în forma cea mai simplă este:tip nume[l1][l2]...[ln]; unde:

l1, l2, ... ln sunt expresii constante care au valori întregi ce pot fi evaluate de compilator la întâlnirea lor.

Evident că se pot declara mai multe tablouri deodată şi atunci numele de tablouri se pot înşirui într-o listă, fiecare separat prin virgulă.

Exemple:int t[5]; // s-a declarat un tablou unidimensional de 5

componente;float a[5][3]; // s-a declarat un tablou bidimensional de 5*3=15

componente;

La elementele unui tablou ne referim prin variabile cu indici. O variabilă cu indici se compune din numele tabloului urmat de unul sau mai mulţi indici, fiecare indice fiind inclus în paranteze drepte. Numărul indicilor defineşte dimensiunea tabloului. Indicii sunt expresii care au valori întregi. Limita inferioară a indicilor este zero. Astfel dacă t este tabloul de tip int în exemplul de mai sus, elementele lui sunt: t[0], t[1], t[2], t[3], t[4].

- 5 -

Page 6: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

În cazul matricii a declarate mai sus elementele ei vor fi:prima linie: a[0][0], a[0][1], a[0][2];a doua linie: a[1][0], a[1][1], a[1][2];...a cincea linie: a[4][0], a[4][1], a[4][2];

Deci pentru dimensiunea k indicele variază între 0 şi lk-1.Repartizarea memoriei pentru un tablou se face astfel: pentru fiecare

element al tabloului se repartizează o zonă de memorie necesară în conformitate cu tipul tabloului respectiv (pentru tipul int câte 2 octeţi, pentru tipul float câte 4 octeţi, etc). Numele unui tablou este un pointer, el poate fi utilizat în diferite construcţii şi el are ca valoare adresa primului său element.

ComentariuÎn limbajul C un comentariu se scrie între /* şi */ pe oricâte rânduri.

între /* şi */ se poate scrie o succesiune arbitrară de caractere, care însă nu poate să conţină secvenţa de terminare (adică */). Comentariul poate fi inserat într-un program în orice poziţie în care este legal să apară un caracter alb. El constituie o explicaţie pentru programator sau pentru utilizator. Compilatorul nu interpretează comentariul în nici un mod. Comentariul se recomandă a fi inserat în punctele în care programatorul poate lămuri prin explicaţii, anumite aspecte ale procesului de calcul sau ale datelor utilizate. În mediile BORLAND C există şi o altă convenţie de a marca un comentariu la nivelul unui rând cu ajutorul a două caractere slash (//), convenţie pe care am folosit-o de altfel în exemplele anterioare.

1.4. PREPROCESAREUn program sursă C poate fi prelucrat înainte de a fi compilat. O astfel

de prelucrare se numeşte preprocesare sau precompilare. Acest lucru se realizează printr-un program special numit preprocesor. Acesta este apelat automat înainte de a începe compilarea.

Preprocesorul limbajului C realizează următoarele:includeri de alte fişiere (de obicei fişiere sursă);definiţii şi apeluri de macrouri simple;compilare condiţionată.

1.4.1. Includerea unui fişier sursăFişierele sursă pot fi incluse cu ajutorul directivei include.Două formate se folosesc pentru a include un fişier sursă în locul în care

apare directiva (de obicei se pune la începutul programului):#include “specificator_de_fisier” sau#include <specificator_de_fisier>

unde: specificator_de_fişier trebuie să fie un nume de fişier valid din punct de vedere al sistemului de operare DOS şi de obicei are extensia “.h” sau “.c”.

Prima variantă este folosită de obicei când fişierele de inclus sunt furnizate de utilizator; dacă nu este indicată calea atunci fişierele sunt căutate în directorul curent şi apoi în directoarele setate pentru directiva include.

A doua variantă este folosită pentru încorporarea unui fişier standard care se caută în directoarele setate pentru directiva include (de obicei astfel de fişiere standard sunt livrate în biblioteci ataşate mediului de programare C).

Odată localizat fişierul dintr-o directivă include se înlocuieşte aceasta prin textul fişierului sursă. Deci compilatorul nu va mai întâlni directiva include ci textul fişierului inclus de preprocesor.

Includerile de fişiere se fac de obicei la început pentru ca textele fişierelor sursă (date şi funcţii) să poată fi utilizate în tot fişierul sursă de lucru. De aceea, la începutul fişierelor sursă vom întâlni mai multe includeri de fişiere standard: stdio.h, stdlib.h, etc. Textul unui fişier inclus poate la rândul său să conţină directiva include. Fişierul stdio.h (prescurtarea de la standard input output header) conţine printre altele şi funcţiile standard de intrare-ieşire printf şi scanf. Fişierele cu extensia “.h” se mai numesc şi fişiere header (fişiere care se pun la începutul fişierului sursă). Un alt

- 6 -

Page 7: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

exemplu de fişier header este iostream.h folosit în mediul BORLAND C care conţine funcţiile cin (console input) şi cout (console output).

1.4.2. Constante simboliceCu directiva define se pot defini constante simbolice şi macrouri.

Constantele simbolice se definesc astfel:

#define nume succesiune_de_caracterePreprocesorul substituie nume cu succesiune_de_caractere peste tot în

fişierul sursă care urmează poziţiei directivei define. Dacă succesiune_de_caractere nu încape pe un rând atunci se poate continua pe rândul următor scriind caracterul “\” la sfârşitul primului rând.

Numele nume definit ca mai sus se spune că este o constantă simbolică. Se recomandă ca nume să fie scris cu litere majuscule pentru a scoate în evidenţă că este o constantă simbolică.

Construcţia succesiune_de_caractere folosită pentru a defini o constantă simbolică poate la rândul ei să conţină alte constante simbolice.

O constantă simbolică poate fi redefinită (tot cu define) sau poate fi anihilată cu undef (#undef nume).

Exemple:1)#define PROCENT 10// din acest punct al fisierului sursa se substituie

// PROCENT cu 10. . .

#define PROCENT 15 // de aici PROCENT se substituie cu 15. . .

#undef PROCENT // din acest punct constanta simbolica PROCENT// isi inceteaza existenta

. . .

#define DIM 100 // s-au definit doua constante simbolice DIM #define DOI_PI (2*3.1415926) // si DOI_PI . . . int vector[DIM]; // DIM va fi inlocuit cu 100 . . .

x=DOI_PI; . . .

- 7 -

Page 8: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- 8 -

Page 9: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 2.V A R I A B I L E

Compilatorul C alocă memorie variabilelor din program de dimensiune corespunzătoare tipului fiecăreia.

Memoria se alocă în 2 moduri:static, repartizată într-o zonă specială asociată programului;dinamic, repartizată într-o zonă specială numită stivă (se comportă ca o

listă LIFO).În funcţie de modul cum se alocă memorie, vom distinge mai multe clase de

variabile.O primă clasă de variabile este aceea a variabilelor globale cărora li se

alocă memorie pe toată durata execuţiei programului şi ele pot fi utilizate în orice funcţie a programului. Altă clasă de variabile este clasa variabilelor locale, aceste variabile au o utilizare locală la nivelul unei funcţii.

2.1. VARIABILE GLOBALEO variabilă globală are o definiţie şi atâtea declaraţii de variabilă

externă câte sunt necesare.Definiţia unei variabile globale coincide sintactic cu o declaraţie

obişnuită, dar care este scrisă în afara oricărei funcţii a programului (fişierului sursă). Implicit, definiţia unei variabile globale determină ca variabila respectivă să fie definită începând din punctul scrierii ei şi până la sfârşitul fişierului sursă respectiv. De aceea se recomandă ca definiţiile variabilelor globale să fie scrise la începutul fişierului sursă, pentru a fi accesate în orice funcţie a fişierului.

Pentru a utiliza variabilele globale şi în alte funcţii situate în alte fişiere sursă decât în cel în care sunt definite, ele trebuie declarate ca externe în funcţiile respective.

O declaraţie de variabilă externă coincide cu o declaraţie obişnuită doar că începe cu cuvântul cheie extern.

Exemplu:fişierul în care sunt declarate ca variabile globale fişierul în care sunt

folosite ca variabile externeint i; float f; void functie(. . .)void main(void) { extern int i;{ i = 10; extern double f; . . . . . . f = 3.14; f = f*i; . . . . . . } }Variabilele i şi f sunt declarate în afara funcţiei main şi în afara

oricărei funcţii, deci sunt variabile globale. Ele pot fi folosite în toate funcţiile din fişierul sursă care conţine definiţiile lor. Pentru a le utiliza în funcţii situate în alte fişiere sursă decât în cel în care sunt definite ele sunt declarate ca externe. Deci variabilele i şi f sunt declarate ca externe în funcţia functie din al doilea fişier sursă. Cele două fişiere sursă care pot fi scrise în momente şi de persoane diferite se pot asambla într-un singur program cu ajutorul directivei de preprocesare include.

2.2. VARIABILE LOCALEVariabilele locale nu sunt valabile în tot programul. Ele au o utilizare

locală în două feluri:ca şi variabile automatice (alocate pe stivă) la nivelul unei funcţii;ca şi variabile statice (alocate în zona programului) la nivel de fişier

(eventual şi la nivelul unei funcţii).Variabilele declarate în interiorul unei funcţii şi care nu sunt declarate

ca externe sunt variabile locale. Lor li se alocă memorie pe stivă la intrarea în funcţia în care sunt declarate. La ieşirea din funcţie, stiva se reface la conţinutul dinaintea apelului şi variabilele locale pierd alocarea. Deci ori de câte ori se apelează o funcţie, variabilele locale acesteia (denumite şi variabile automatice) se alocă (primesc memorie) pe stivă şi la ieşirea din funcţie variabilele locale sunt şterse din stivă.

- 9 -

Page 10: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Variabilele locale pot să nu fie alocate pe stivă, ci într-o zonă de memorie destinată acestui scop. O astfel de variabilă locală se numeşte variabilă statică. O variabilă locală statică se declară printr-o declaraţie obişnuită, precedată de cuvântul cheie static. O variabilă statică poate fi declarată atât în corpul unei funcţii cât şi în afara oricărei funcţii. În cazul unei variabile statice declarată în interiorul unei funcţii alocarea nu se face pe stivă ci într-o zonă de memorie destinată acestui scop, aceasta fiind deosebirea esenţială faţă de variabilele automatice. În cazul în care o variabilă statică este declarată în afara funcţiilor, ea este definită din punctul în care a fost declarată şi până la sfârşitul fişierului sursă care conţine declaraţia ei. Spre deosebire de variabilele globale, o variabilă statică nu poate fi declarată ca externă şi deci nu poate fi utilizată în funcţiile din alte fişiere sursă diferite de cel în care a fost declarată.

Se recomandă ca tablourile mari să fie declarate statice, deoarece dacă sunt automatice pot depăşi capacitatea stivei (care are implicit 4K octeţi).

Exemple:Fişierul fisier1.c este un fişier sursă care conţine 2 variabile globale i

şi d , o variabilă statică x şi două funcţii f şi main. Funcţia main conţine variabila statică a iar funcţia f conţine variabila statică b.

int i; // definiţia variabilei globale idouble d; // definiţia variabilei globale dstatic int x; // definiţia variabilei statice x, locala

fisierului fisier1.c

void main (void){ static char a; // definiţia variabilei statice a, locala funcţiei main float c; // definiţia variabilei automatice c, locala funcţiei main /* in acest moment se pot folosi variabilele i,d,x,a si c */ . . . }

{ int p; // definiia variabilei automatice p, locala funciei f static float b; // definiia variabilei statice b, locala funciei f /* in acest moment se pot folosi variabilele i,d,x, p si b */ . . . }Variabilele a şi c fiind locale funcţiei main nu pot fi utilizate în

funcţia f. Analog, variabilele p şi b sunt locale în funcţia f, nu pot fi utilizate în funcţia main.

Fişierul fisier2.c conţine funcţiile f1 şi f2 care intră în componenţa aceluiaşi program ca şi funcţiile main şi f din fişierul fisier1.c

static unsigned t; // definitia variabilei statice t, locala fisierului fisier2.c

void f1(...){ extern int i; // declaratie externa pentru i extern double d; // declaratie externa pentru d static int k; // definitia variabilei statice k, locala functiei f1 /* in acest moment se pot folosi varibilele i,d, k si t */ . . . }

void f2(...) { extern int i; // declaratie externa pentru i static double s; // definitia variabilei statice s, locala functiei f2 /* se pot folosi variabilele i, s si t */ . . . } Variabila statică x definită în fişierul fisier1.c nu poate fi

utilizată în fişierul fisier2.c. De asemenea, variabila statică t nu poate fi utilizată în fişierul fisier1.c. Variabila globală d nu poate fi utilizată în funcţia f2, ea nefiind declarată ca şi externă.

- 10 -

Page 11: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Observaţii:1o. Variabilele globale constituie un mijloc simplu de interfaţă între

funcţiile unui program. Se recomandă a fi folosite când dorim transferuri de valori între două sau mai multe funcţii în aşa fel încât modificările realizate de o funcţie să fie accesibile pentru toate funcţiile programului. Nu trebuie să se facă abuz în utilizarea variabilelor globale deoarece constituie şi o sursă potenţială de erori, pentru că accesul unui mare număr de funcţii la anumite date globale conduce deseori la modificări nedorite şi greu evidenţiabile.

2o. Funcţiile sunt considerate cu atributul implicit extern. Dacă într-un program există mai multe fişiere sursă atunci o funcţie poate fi apelată oriunde, bine înţeles respectând convenţia definirii ei sau a prototipului ei înainte de a fi folosită. Putem să limităm definind funcţiile cu atributul static precedând antetul funcţiei prin cuvântul cheie static. Astfel funcţia respectivă devine locală şi deci apelabilă doar în fişierul în care este definită.

2.4. INIŢIALIZAREVariabilelor li se pot atribui valori iniţiale. Pentru variabilele globale

valorile iniţiale se atribuie prin definiţiile lor, iar în cazul celorlalte variabile se pot atribui valori prin declaraţiile lor. Pentru că de multe ori am folosit cuvintele definiţia variabilelor sau declaraţiile varibilelor precizăm că ele au înţeles distinct în anumite contexte. O variabilă globală se defineşte o singură dată şi se poate declara ori de câte ori e necesară utilizarea ei în alte fişiere (evident declarată externă). În cazul celorlalte tipuri de variabile definiţiile şi declaraţiile coincid. Totodată definiţia şi declaraţia (prototipul) unei funcţii nu coincid.

O variabilă simplă se poate iniţializa printr-o declaraţie de forma:

tip nume=expresie;Variabilelor globale şi statice li se atribuie valori iniţiale la lansarea

programului. Expresia utilizată în cazul acestor variabile trebuie să fie o expresie constantă care să poată fi evaluată de compilator la întâlnirea ei. Aceasta, deoarece variabilele globale şi statice se iniţializează prin valori definite la compilare.

Variabilele automatice se iniţializează la execuţie, de fiecare dată când se activează funcţia în care sunt declarate. Din această cauză, nu mai este necesar ca expresia să fie o expresie constantă. Totuşi, la întâlnirea ei, trebuie să fie definiţi operanzii expresiei de iniţializare.

Exemplu:void f(int n){ int i=10; int k=i+n; . . .}La întâlnirea expresiei i+n sunt deja definiţi ambii operanzi:i a fost în prealabil iniţializat cu valoarea 10;n are o valoare care e transferată la apel.Variabilele globale şi statice neiniţializate au implicit valoarea egală

cu zero, iar varibilele automatice neiniţializate au o valoare iniţială imprevizibilă.

Tablourile se pot iniţializa printr-o listă de expresii incluse între acolade. Astfel un tablou unidimensional se poate iniţializa folosind următorul format:

tip nume[n] = { exp1, exp2, . . . expn }

La iniţializarea tablourilor se pot utiliza numai expresii constante. Numărul expresiilor poate fi mai mic decât numărul elementelor tabloului. Elementele neiniţializate au valoarea zero în cazul tablourilor globale şi statice. Dacă se iniţializează fiecare element al tabloului atunci numărul n al elementelor acestuia nu mai este obligatoriu în declaraţia tabloului respectiv. Deci se poate scrie astfel:

tip nume[ ] = { exp1, exp2, . . . expn};

- 11 -

Page 12: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Numărul elementelor tabloului se consideră că este egal cu cel al expresiilor care realizează iniţializarea lor.

Pentru un tablou bidimensional vom folosi următoarea structură:

tip nume [n][m] = { {exp11, exp12, . . . exp1m}, // pentru linia întâi {exp21, exp22, . . . exp2m}, // pentru linia a doua . . . {expn1, expn2, . . . expnm}, // pentru ultima linie };Numărul expresiilor poate fi mai mic decât m în oricare din acoladele

corespunzătoare celor n linii ale tabloului bidimensional. În acest caz se poate omite doar numărul n (al liniilor tablourilor), m fiind obligatoriu. Formatul de iniţializare a tablourilor bidimensionale se extinde imediat pentru tablouri cu mai multe dimensiuni.

Tablourile de tip caracter pot fi iniţializate folosind un format mai simplu decât cel indicat anterior. Astfel. un tablou de tip caracter se poate iniţializa printr-o declaraţie de forma:

char sir[n] = şir_de_caractere;

Evident, marginea superioară n poate fi omisă şi în acest caz. Totodată compilatorul pune automat caracterul NUL după ultimul caracter al şirului utilizat în iniţializare.

Exemple:1)int itab[] = {1,2,3,4,5} // tabloul de tip intreg are 5 elemente

itab[0] = 1,. . . itab[4] = 5

2)int m[3][3] = {{-1,0,1},{-1},{0,1}};// tabloul are 3 linii si 3 coloane. Elementele primei linii sunt iniţializate astfel:m[0][0] = –1;m[0][1] = 0;m[0][2] = 1;În linia a doua se iniţializează numai m[1][0] = -1; Dacă tabloul ar fi

declarat global sau static atunci m[1][1] şi m[1][2] ar avea valoarea zero. Altfel ele au o valoare imprevizibilă.

Elementele ultimei linii se iniţializează astfel: m[2][0] = 0; m[2][1] = 1;declaraţiile de mai jos sunt identice:char sir [ ] = {‘L’,’I’,’M’,’B’,’A’,’J’,’U’,’L’, ‘ ‘,‘C’};char sir [ ] = {“LIMBAJUL C”};

- 12 -

Page 13: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 3.EXPRESII, OPERANZI, OPERATORI

3.1. EXPRESIIO expresie în limbajul C este formată fie dintr-un operand fie din mai

mulţi operanzi legaţi între ei prin operatori. O expresie are o valoare şi un tip care se determină aplicând operatorii conform priorităţilor şi asociativităţii acestora.

În limbajul C operatorii se asociază de la stânga la dreapta, exceptând operatorii unari şi de atribuire, care se asociază de la dreapta la stânga.. Totodată pot fi folosite parantezele rotunde pentru a impune o anumită ordine în executarea operaţiilor.

3.2. OPERANZIUn operand în limbajul C poate fi una din următoarele elemente:o constantă;o constantă simbolică;numele unei variabile simple;numele unui tablou;numele unei structuri;numele unei funcţii;referirea la elementul unui tablou (variabilă cu indici);referirea la elementul unei structuri;apelul unei funcţii;o expresie inclusă în paranteze rotunde.Unele dintre elementele de mai sus nu au fost încă definite, ele se vor

prezenta în lecţiile viitoare.Exemple:9876 - constantă întreagă;x - variabilă simplă;t[i][3] - variabilă cu indici;0xabcd - constantă hexazecimală;t - nume de tablou;expresie) - expresie inclusă în paranteze rotunde.f1 - numele unei funcţii

3.3. OPERATORIOperatorii limbajului C pot fi grupaţi în mai multe clase, dar oricum ei

pot fi folosiţi împreună într-o aceeaşi expresie. Operatorii au arităţi diferite: unari, binari, ternari şi totodată o anumită prioritate implicită care e redată în tabelul de mai jos. Operatorii de aceeaşi prioritate se află trecuţi în aceeaşi linie. Liniile tabelulul conţin operatorii limbajului C în ordinea descrescătoare a priorităţilor. Astfel în prima linie se află operatorii de prioritate maximă, iar în ultima linie operatorul virgulă cu prioritatea cea mai mică. Cu excepţia operatorilor “.”, “->”,”&”,”*”, a parantezelor rotunde (folosite la definiţia şi apelul funcţiilor) şi a parantezelor drepte (folosite la variabilele cu indici) ceilalţi operatori vor fi explicaţi în această lecţie.

( ) [ ] . ->- (unar) +(unar) *(unar) &(unar) ! ~ ++ -- (tip) sizeof

* / %+ -<< >>< <= >= >= = !=&^|&&| |? : (ternar)= op= op poate fi: *(binar) / % +(binar) –(binar) << >> & ^ |,

- 13 -

Page 14: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

3.3.1. Operatori aritmeticiLista operatorilor aritmetici este redată mai jos: - (minus unar); + (plus unar); * / % operatori binari multiplicativi; (înmulţire, împărţire, restul

împărţirii întregi); + - operatori binari aditivi (adunare şi scădere).Operatorii de pe aceeaşi linie au aceeaşi prioritate. Cei unari au

prioritate mai mare decât cei binari. Operatorii multiplicativi au prioritate mai mare decât cei aditivi.

Exemple:int i,j,k;float x,y;double t[10];

// se dau cateva exemple de expresii folosind operatorii aritmeticii*x+t[5];-y+k;i%j; // daca i=9 si j=4 atunci i%j are valoarea 1i/j; // daca i=9 si j=4 atunci i/j are valoarea 2x*-y; // - este operatorul unar deci avem x*(-y)

3.3.2. Operatori relaţionaliLista operatorilor relaţionali este redată astfel:< (mai mic)<= (mai mic sau egal; cele două caractere ce compun operatorul sunt

concatenate) > (mai mare)>= (mai mare sau egal; cele două caractere ce compun operatorul sunt

concatenate)Toţi operatorii relaţionali au aceeaşi prioritate. Ea este mai mică decât

prioritatea operatorilor aditivi. Rezultatul aplicării unui operator relaţional este 1 sau 0, după cum operanzii se află în relaţia definită de operatorul respectiv sau nu.

Exemple:a= 4 şi b= -5 atuncia>0 are valoarea 1;a<=0 are valoarea 0;a+b>0 are valoarea 0;a>=b are valoarea 1;a<0 are valoarea 1;a+b>=b-a are valoarea 1;a+b<=(b-a)*(b-a) are valoarea 0;

3.3.3. Operatori de egalitateLista operatorilor de egalitate este redată mai jos:= = (egal; două semne “=” concatenate)!= (diferit; semnele sunt concatenate).Operatorii de egalitate au ambii aceeaşi prioritate şi este imediat mai

mică decât a operatorilor relaţionali. Operatorul “= =” testează egalitatea a doi operanzi. Dacă operanzii sunt egali atunci rezultatul operaţiei “= =” este 1, în caz contrar este 0. Operatorul “!=” furnizează rezultatul 1 când cei doi operanzi sunt diferiţi şi 0 când sunt egali.

Exemple:a= 2 şi b=-1atunci

a==b are valoarea 0;a!=b are valoarea 1;a*b!=a+b are valoarea 1;

3.3.4. Operatori logiciLista operatorilor logici este redată mai jos:! (negaţia logică - operator unar);&& (ŞI logic);|| (SAU logic).

- 14 -

Page 15: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Operatorul “!” are aceeaşi prioritate cu operatorii unari “+” şi “-“. Operatorul “&&” este mai prioritar decât operatorul “||”, dar are o prioritate mai mică decât operatorii de egalitate.

În limbajul C nu există valori logice speciale. Valoarea fals se reprezintă prin zero. Orice valoare diferită de zero reprezintă valoarea adevărat.

Dacă operatorul “!” se aplică la un operand a cărui valoare este zero, atunci rezultatul este 1. Dacă acelaşi operator se aplică la un operand a cărui valoare este diferită de zero, atunci rezultatul este 0.

Chiar dacă pentru “sau exclusiv” nu există operator el se poate realiza prin expresia următoare aplicată operanzilor a şi b: !a&&b||!b&&a sau folosind parantezele rotunde ((!a) &&b)||((!b)&&a).

Operatorii logici se evaluează de la stânga la dreapta. Dacă la evaluarea unei expresii se ajunge într-un punct în care se cunoaşte valoarea întregii expresii, atunci restul expresiei nu se mai evaluează.

Dacă a=0 şi b=1 atunci expresia ! a||b are valoarea 1 pentru că !a are deja valoarea 1.

3.3.5. Operatori logici pe biţiLista operatorilor logici pe biţi este redată mai jos în ordinea

descrecătoare a priorităţilor:~ (operator unar; complement faţă de 1)>> << (deplasări la dreapta, respectiv la stânga)& (ŞI pe biţi)^ (SAU-EXCLUSIV pe biţi)| (SAU pe biţi)Operatorul “~”, fiind unar, are aceeaşi prioritate ca şi ceilalţi

operatori unari ai limbajului C. El schimbă fiecare bit 1 al operandului în 0 şi invers.

Operatorul “>>” realizează deplasarea la dreapta care este echivalentă cu o împărţire întreagă cu puteri a lui 2; a >> 3 este echivalentă cu [a/23].

Operatorul “<<” realizează deplasarea la stânga care este echivalentă cu o înmulţire cu puteri a lui 2; a << 3 este echivalentă cu a*8.

Observaţii:1o.Operanzii care nu ocupă un cuvânt (16 biţi) se extind la un cuvânt. De

exemplu expresia ~0 are ca rezultat un cuvânt cu toţi biţi egali cu 1.2o.Operatorii logici pe biţi se execută bit cu bit spre deosebire de

operatorii logici care se evaluează global. De exemplu dacă x=2 i y=1 sunt variabile de tip int atunci:

x&&y are valoarea 1 pentru că ambii operanzi sunt diferiţi de 0. x&y are valoarea 0 conform schemei de mai jos 0000 0000 0000 0010 0000 0000 0000 0001

& 0000 0000 0000 0000 3o.Operatorul & se foloseşte frecvent pentru a anula biţi din

configuraţia unui cuvânt, iar operatorul | pentru a seta (pune) biţi într-un anumit mod;

4o. Operanzii trebuie să fie întregi (de tipul int sau long).5o. Atenţie la deplasări nu se modifică valoarea operandului; deci trebuie

să facem o atribuire; de exemplu a = a << 3 va modifica valoarea lui a pe când a << 3 nu modifică valoarea lui a.

Exemple:Fie declaraţia:int i;atunci expresia i >> 8 & 255 are ca rezultat valoarea celui mai

semnificativ octet a lui i; i >> 8 deplasează octetul mai semnificativ al lui i în poziţia mai puţin semnificativă; se face apoi un ŞI logic pe biţi cu masca 255 care păstrează octetul mai puţin semnificativ.

2) Fie expresia: (x >> 6) & ~(~ 0 << 3) Să presupunem că x are valoarea în biţi 1010 1011 1000 1101. Atunci x>>6 are valoarea 1111 1110 1010 1110 Al doilea operand pregăteşte o mască astfel:

- 15 -

Page 16: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

~0 1111 1111 1111 1111 ~0<<3 1111 1111 1111 1000 ~(~0<<3) 0000 0000 0000 0110

Rezultatul final este dat de: 0000 0000 0000 0111

1111 1110 1010 11100000 0000 0000 0110

Practic s-a obţinut valoarea biţilor 8,7,6 a lui x (numerotaţi de la dreapta la stânga începând cu 0).

3.3.6. Operatori de atribuireÎn forma cea mai simplă operatorul de atribuire se notează cu “=” şi se

utilizează în construcţii de forma: v=expresie;

(v este fie o variabilă simplă, fie variabilă cu indici sau un element de structură).

Această construcţie se mai numeşte expresie de atribuire. Ea este considerată ca fiind un caz particular de expresie. Tipul ei coincide cu tipul lui v, iar valoarea întregii expresii este chiar valoarea atribuită lui v.

O expresie de forma:v1=(v=expresie);

este şi ea legală şi se efectuează în felul următor :se evaluează expresia expresie şi valoarea ei se atribuie lui v;valoarea lui v se atribuie apoi şi lui v1.Deoarece operatorii de atribuire se asociază de la dreapta la stânga,

expresia de mai sus se poate scrie şi fără paranteze:

v1=v=expresie;În general, putem realiza atribuiri multiple printr-o expresie de forma:

vn =. . . =v1=v=expresieDacă expresia din dreapta semnului egal are un tip diferit de cel al

variabilei v, atunci întâi valoarea ei se converteşte spre tipul variabilei v şi pe urmă se realizează atribuirea,

Pentru operaţia de atribuire, în afara semnului egal se mai poate folosi şi succesiunea :

op=unde prin op se înţelege unul din operatorii binari aritmetici sau logici

pe biţi, adică unul din următorii:% / * - + & ^ | << >>

Acest mod de construcţie se foloseşte pentru a compacta un anumit tip de atribuire. Astfel expresia:

v op = expresie;este identică cu expresia de atribuire:

v = op expresie;Exemple:

int i, j;double x, y;int v[10];i=5;j=10;x=y=10.01;i +=1; // echivalenta cu i=i+1 si cu i++x*=3; // echivalenta cu x=x*3j<<=10; // echivalenta cu j=j<<10v[i]*=i // echivalenta cu v[i]=v[i]*ix /= x-y // echivalenta cu x = x/(x-y)

- 16 -

Page 17: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

3.3.7. Operatori de incrementare şi decrementareAceşti operatori sunt unari şi au aceeaşi prioritate cu ceilalţi operatori

unari ai limbajului C. Operatorul de incrementare se notează prin “++” şi măreşte valoarea operandului cu unu, iar operatorul de decrementare se notează prin “- -“ şi micşorează valoarea operandului cu unu. Operatorii sunt folosiţi prefixat şi postfixat. Astfel operatorii prefixaţi au notaţia:

++operand;- - operand;

Ei se aplică mai întâi şi apoi se foloseşte valoarea lor.Astfel operatorii postfixaţi au notaţia:operand++;operand - -;

Se foloseşte valoarea operanzilor şi apoi se aplică incrementarea sau decrementarea.

Menţionăm că aceşti operatori se pot aplica numai la următorii operanzi:variabilă simplă;variabilă cu indici;referire la elementul unei structuri.Exemple:

int i,j;double x,y;int vector [5];j=i++; // este echivalent cu j=i si i=i+1; y=--x; // este echivalent cu x=x-1 si y=x;i=++vector[j] // este echivalent cu vector[j]=vector[j]+1 si

i=vector[j]

3.3.8. Operatorul de conversie explicită (expresie cast)Pentru forţarea tipului unui operand se foloseşte o construcţie de forma:

(tip) operandPrin aceasta valoarea operandului se converteşte spre tipul indicat în

paranteze.Exemplu:

int i,j;double y;i=8; j=5;y=i/j;// y are valoarea 1, pentru ca se face impartirea intreaga i/j

Dacă vom converti operanzii i şi j spre tipul double se va obţine rezultatul corect adică 1.6.

Deci:int i,j;

double y;i=8; j=5;y=(double) i / (double) j; // y are valoarea 1.6,

Construcţia (tip) este un operator unar prin care se explicitează conversia dorită. Are aceeaşi prioritate ca restul operatorilor unari.

3.3.9. Operatorul dimensiune (sizeof)Pentru a determina lungimea în octeţi a unei date se poate folosi

construcţia:

sizeof (data)unde data poate fi:numele unei variabile simple;numele unui tablou;numele unei structuri;numele unui tip;referirea la elementul unui tablou sau structură.

- 17 -

Page 18: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Exemple:int i;long l;float f;double d;

char c;int itablou[5];double dtablou[5];sizeof (i) // are valoarea 2;sizeof (l) // are valoarea 4;sizeof (f) // are valoarea 4;sizeof (d) // are valoarea 8;sizeof (c) // are valoarea 1;sizeof (itablou[1]) // are valoarea 2;sizeof (dtablou[1]) // are valoarea 8;sizeof (itablou) // are valoarea 10;sizeof (dtablou) // are valoarea 40;

Regula conversiilor impliciteÎn general o expresie C conţine operanzi de tipuri diferite. Pentru

operatorii binari există situaţii când operanzii nu sunt de acelaşi tip şi trebuie executate conversii astfel încât operatorii să se aplice pentru operanzi de acelaşi tip. Aceste conversii le face automat compilatorul. Există o regulă a conversiilor implicite care are următorii paşi:

• fiecare operand de tip char se converteşte spre tipul int şi fiecare operand de tipul float se converteşte spre double;

• dacă unul dintre operanzi este de tip double atunci şi celălalt se converteşte spre tipul double şi rezultatul va avea tipul double;

• dacă unul dintre operanzi este de tip long, atunci şi celălalt se converteşte spre tipul long şi rezultatul va avea tipul long;

• dacă unul dintre operanzi este de tip unsigned, atunci şi celălalt se converteşte spre tipul unsigned şi rezultatul va fi de tipul unsigned;

• la acest pas se ajunge numai dacă ambii operanzi sunt de tip int şi deci operaţia se execută cu operanzii respectivi, iar rezultatul va fi de tip int.

Aplicând regula de mai sus pas cu pas (la fiecare operator în momentul efectuării lui), se ajunge în final la evaluarea întregii expresii şi prin acesta se determină tipul expresiei. Regula conversiilor implicite nu se aplică pentru operatorul de atribuire (valoarea expresiei din partea dreaptă a semnului de atribuire se converteşte spre tipul variabilei din stânga semnului egal).

Exemple:int i, j, k;float a, b;double x, y;unsigned p;long r;char c;expresii conversii tipul expresiei

i-j/k nu int a/b a spre double

b spre double doublex+y nu doublei+a a spre double i spre doublei-3.14 i spre double double i+3 nu inti+x i spre double doublei-c c spre int intx+10 10 spre double doublep-10 10 spre unsigned unsignedr*5 5 spre long long(double)(i/j) se realizează împărţirea întreagă între

i şi j şi rezultatul se converteşte spre double double

- 18 -

Page 19: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Dacă rezultatul unei operaţii depăşeşte domeniul de valori ce corespunde tipului rezultatului, valoarea respectivă se trunchiază şi rezultatul este eronat.

3.3.11. Operatori condiţionaliOperatorii condiţionali sunt ? şi : şi se folosesc împreună în construcţii

de forma:exp1 ? exp2 : exp3Evaluarea se face astfel:se evaluează expresia exp1;dacă exp1 este diferită de zero, atunci valoarea şi tipul expresiei

condiţionale sunt egale cu valoarea şi tipul expresiei exp2; altfel cu expresia exp3.

Exemplu: procesul de determinare a maximului a două numere a şi b este:dacă a>b atunci max=a

altfel max=bsfdacă

În limbajul C se poate realiza acest proces cu ajutorul operatorilor condiţionali astfel:

max= a>b ? a : bDacă a>b atunci expresia condiţională are valoarea şi tipul lui a altfel

expresia condiţională are valoarea şi tipul lui b.

Operatorul virgulăOperatorul “,” este folosit pentru gruparea mai multor expresii într-una

singură.Cu ajutorul acestui operator (care are prioritatea cea mai mică) se

construiesc expresii de forma:exp1, exp2,. . ., expn

Această expresie are valoarea şi tipul ultimei expresii (deci a lui expn).Exemplu:

k= (i=10, j=j+5; i+j)Se execută pe rând cele două atribuiri de la stânga la dreapta din

parantezele rotunde apoi se face suma i+j şi în final se atribuie această sumă lui k,

- 19 -

Page 20: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- 20 -

Page 21: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 4.INTRĂRI / IEŞIRI STANDARD

Limbajul C nu posedă instrucţiuni de intrare / ieşire. Aceste operaţii se realizează prin apeluri de funcţii din bibliotecile standard ale mediului de programare. De obicei astfel de funcţii asigură interfaţa programului cu terminalul de la care s-a lansat, cu imprimanta, etc. Se numesc intrări standard şi ieşiri standard intrările respectiv ieşirile de la terminalul de la care s-a lansat programul. Totodată se presupune că datele de intrare / ieşire sunt organizate în fişiere.

Unui program C i se ataşează în mod implicit următoarele fişiere:

- stdin intrare standard;- stdout ieşire standard;- stderr ieşire standard pentru erori;- stdprn ieşire pe imprimantă;- stdaux intrare / ieşire serială.

4.1. FUNCŢIA STANDARD printf Funcţia printf realizează ieşiri cu format la ieşirea standard stdout,

deci afişare la terminalul la care care s-a lansat programul. Funcţia printf se apelează printr-o instrucţiune cu formatul:

int printf (control, lista_expresii);

unde control este un şir de caractere care conţine:texte de scris;specificatori de format pentru datele care se scriu din lista_expresii.lista_expresii conţine expresii; valoarea fiecărei expresii se scrie

conform unui specificator de format corespondent din parametrul control.Parametrul control este inclus între ghilimele, iar numărul

specificatorilor de format coincide cu numărul expresiilor din lista_expresii. Dacă dorim să scriem numai un text atunci parametrul de control nu conţine nici un specificator de format iar lista_expresii este vidă (practic este absentă).

Un specificator de format are formatul BNF următor:

%[-][d..d][.d..d][l1]l2

După cum se observă un specificator de format începe întotdeauna cu caracterul procent (“%”). După acest caracter poate urma una din construcţiile următoare:

- un caracter “-“ opţional; prezenţa acestui caracter indică cadrarea la stânga a datei în câmpul în care se scrie (implicit data se scrie cadrată la dreapta);

-un şir de cifre zecimale opţional, care defineşte lungimea minimă a câmpului în care se scrie data corespunzătoare din lista_expresii; data se scrie astfel:

în cazul în care necesită o lungime mai mică se scrie cadrată la dreapta sau stânga (în funcţie de absenţa sau prezenţa semnului “-“)

în cazul în care necesită o lungime mai mare se va scrie pe atâtea poziţii câte îi sunt necesare;

-un punct urmat de un şir de cifre zecimale (după cum se observă opţional); acest element indică precizia datei care se scrie:

dacă data se scrie în virgulă flotantă, precizia defineşte numărul de cifre aflate după marca zecimală (deci câte zecimale);

dacă data este un şir de caractere atunci indică câte caractere se scriu.-una sau două litere, care definesc tipul de conversie din formatul intern

în formatul extern:prima litera poate fi “l”, ceea ce semnifică conversia din formatul intern

long în formatul extern definit de specificator;a doua literă este obligatorie întotdeauna şi are semnificaţiile de mai

jos:

- 21 -

Page 22: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

litera tipul de conversie realizat d - din int intern în zecimal externo - din int intern în octal externx - din int intern în hexazecimal extern (litere mici pentru

cifrele mai mari ca 9 deci a,b,c,d,e,f,)X - din int intern în hexazecimal extern (litere mici pentru

cifrele mai mari ca 9 deci A,B,C,D,E,F)u - din unsigned intern în zecimal extern fără semnc - din binar intern (cod ASCII) în caracterul corespunzătors - din şir de coduri ASCII într-un şir de caractere (atenţie ultimul cod este NUL (adică ‘\0’)f - din float sau double intern în d...d.d...d (implicit 6 cifre zecimale la partea fracţionară dacă nu e prezentă precizia)e - din float sau double intern în d.d...de±ddd (implicit 6 cifre zecimale la partea fracţionară dacă nu e prezentă precizia)E - din float sau double intern în d.d...dE±ddd (implicit 6 cifre zecimale la partea fracţionară dacă nu e prezentă precizia)g - se aplică una din conversiile definite de literele f şi e alegându-se aceea care are un număr minim de poziţiiG - se aplică una din conversiile definite de literele f şi E alegându-se aceea care are un număr minim de poziţii

Literele d, o, x, u pot fi precedate de litera l conversia realizându-se din formatul intern long în loc de int.

Observaţie. 1o. Dacă caracterul % nu este urmat de una din construcţiile de mai sus

atunci nu reprezintă un specificator de format.Funcţia printf întoarce lungimea totală în octeţi a datelor scrise

la terminal sau valoarea simbolică EOF în caz de eroare. Precizăm că EOF este definită în fişierul header stdio.h astfel:

#define EOF –1.

Totodată funcţia printf poate fi testată şi astfel:EOF= = printf (control, lista_expresii)Dacă are valoarea adevărat atunci la scrierea datelor s-a produs o eroare.

Exemple:1)#include<stdio.h>#include<math.h>void main(void){ int i=10; long j=11; float a=1.2, b=1.3; double A=1.4; B=1.5; clrscr(); // inceputul instructiunilor executabile; se sterge ecranul printf ("\ni*j = %d",i*j); // incep afisarile printf ("\ni*j = %5d",i*j); printf ("\ni*j = %-5d",i*j); printf ("\ni*j = %5.5d",i*j); printf ("\ni*j = %05d",i*j); printf ("\na*b = %10.1f",a*b); printf ("\nA*B = %10.5lf",A*B); printf ("\nradical(a*b) = %lf",sqrt((double) a*b)); printf ("\nradical(A*B) = %15.10lf",sqrt(A*B)); printf ("\nradical(A*B) = %25.17lf",sqrt(A*B)); printf ("\nradical(A*B) = %25.19lf",sqrt(A*B)); getche(); // asteapta citirea unui caracter de la terminal}Rezultatele execu iei programului sunt: i*j = 110i*j = 110i*j = 110i*j = 00110

i*j = 00110a*b = 1.6A*B = 2.10000radical(a*b) = 1.249000

- 22 -

Page 23: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

radical(A*B) = 1.4491376746radical(A*B) = 1.44913767461894372

radical(A*B) = 1.4491376746189437200

2)#define sir “PC WORLD”void main (void){ printf(“*%10s*“,sir); printf(“*%-10s*“,sir); printf(“*%10.5s*“,sir); printf(“*%-10.5s*“,sir);}Rezultatele execuţiei programului sunt:* PC WORLD**PC WORLD ** PC WO*

*PC WO *

4.2. FUNCŢIA STANDARD scanf Funcţia de bibliotecă scanf realizează intrări cu format de la intrarea

standard stdin (intrare de la terminalul de la care s-a lansat programul) şi poate fi apelată printr-o instrucţiune de apel de forma:

scanf (control, lista_adrese);Ca şi în cazul funcţiei printf, parametrul control este delimitat de

ghilimele şi poate conţine texte şi specificatori de format. Caracterele albe din parametrul control sunt neglijate. Celelalte caractere care nu fac parte dintr-un specificator de format, trebuie să existe la intrare în poziţii corespunzătoare. Ele se folosesc în scopul realizării unor verificări asupra datelor care se citesc.

Un specificator de format începe şi în acest caz prin caracterul procent, apoi:

un caracter “*” opţional;un şir de cifre, opţional, care defineşte lungimea maximă a câmpului din

care se citeşte data de sub controlul specificatorului de format;una sau două litere care definesc tipul conversiei din formatul extern în

formatul intern.Câmpul controlat de un specificator de format începe cu primul caracter

care nu este alb şi se termină:fie la caracterul după care urmează un caracter alb;fie la caracterul după care urmează un caracter care nu corespunde

specificatorului de format care controlează acel câmp;fie la caracterul prin care se ajunge la lungimea maximă indicată în

specificatorul de format.Condiţia c) este absentă în definirea câmpului dacă în specificatorul de

format nu este indicată lungimea maximă a câmpului.Literele care termină un specificator de format sunt asemănătoare cu

cele utilizate la funcţia printf. În acest caz este realizată conversia inversă faţă de cea realizată de funcţia printf.

Litera Tipul conversiei realizate d - data din câmpul de intrare este un şir de cifre zecimale, precedat

eventual de un semn şi se converteşte din zecimal extern în binar de tip into - ca şi în cazul literei d, dar din octal extern în binar de tip int.x - ca şi în cazul literei d, dar din hexazecimal extern în binar de tip

int; se utilizează literele mici a-f sau mari A-F pentru cifrele mai mari ca 9.X - ca şi în cazul literei x;u - data este un şir de cifre zecimale care formează un număr întreg fără

semn şi se converteşte din zecimal extern în binar tipul unsigned.c - data se consideră formată din caracterul curent de la intrare şi

parametrului corespunzător i se atribuie codul ASCII al acestui caracter; în acest caz nu se face avans peste caracterele albe, ca şi în cazul celorlalţi specificatori.

s - data se consideră că este şir de caractere; şirul începe, conform regulii generale, cu primul caracter care nu este alb şi se termină la caracterul după care urmează un caracter alb sau când s-au citit atâtea caractere câte indică dimensiunea maximă din specificatorul de format;

- 23 -

Page 24: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

f - data de intrare reprezintă un număr flotant; deci conversie din flotant extern în virgulă flotantă simplă precizie; data care se citeşte conţine un punct sau un exponent, sau atât punct cât şi exponent.

Literele d, o, x şi u pot fi precedate de litera l şi în acest caz conversia se realizează spre long, în loc de int.

De asemenea, litera f va fi precedată de litera l pentru a face conversii spre formatul virgulă flotantă dublă precizie.

Lista_adrese conţine adresele zonelor receptoare ale datelor citite prin intermediul funcţiei scanf. Fiecare dintre parametri reprezintă adresa unei zone receptoare sub forma:

&nume

Ea determină adresa zonei de memorie rezervată variabilei nume. Caracterul “&” din construcţia de mai sus reprezintă un operator unar, numit operatorul adresă. El are aceeaşi prioritate ca şi ceilalţi operatori unari din limbajul C.

Exemplu:void main (void){ int i; long n; float x; scanf (“ %d %ld %f ”,&i,&n,&x); // citeste datele din stdin si le

atribuie lui i,n,x. scanf(“ %d %*ld %f “, &i,&x); // caracterul * indica faptul ca

valoarea pentru variabila n nu se citeste}Funcţia scanf returnează numărul de câmpuri citite corect din fişierul

stdin. Dacă apare o eroare la citire (din diverse motive de exemplu neconcordanţă dintre specificatorul de format şi datele din fişierul stdin) atunci funcţia nu va returna numărul total de câmpuri; citirea se va întrerupe în locul detectării erorii şi scanf va returna numărul de câmpuri citite până în acel moment. Deci de multe ori pentru a valida formal intrarea datelor (atenţie nu e o validare calitativă) se va folosi o construcţie de forma:

nr=scanf(...)

Prin construcţia de mai sus se poate pune în evidenţă sfârşitul de fişier, deoarece în acest caz scanf returnează valoarea EOF. Sfârşitul de fişier se poate genera de la tastatură acţionând în acelaşi timp tastele CTRL şi Z (deci CTRL / Z). Deorece funcţia scanf citeşte datele de la intrarea standard prin intermediul unei zone de memorie intermediare (numită zonă tampon sau buffer), ea are acces la caracterele din zona tampon numai după acţionarea tastei ENTER (RETURN). De aceea după acţionarea combinaţiei CTRL / Z se va tasta şi ENTER. Totodată această intermediere face posibilă eventuale corecturi înainte de a acţiona tasta ENTER.

Exemplu: Vom citi un întreg de cinci cifre de la intrarea standard şi vom afişa

cifrele respective precedate fiecare de două spaţii.

void main (void) { int c1,c2,c3,c4,c5; // c1,c2,c3,c4,c5 sunt cifrele intregului citit scanf(“%1d %1d %1d %1d %1d”, &c1,&c2,&c3,&c4,&c5);

// se citesc cifrele intregului in cele 5 variabile cu %1d printf(“%3d%3d%3d%3d%3d”,c1,c2,c3,c4,c5);}

Pentru a citi şiruri de caractere se va folosi funcţia scanf fără a mai pune operatorul de adresă în faţa numelui şirului de caractere, deoarece numele unui tablou reprezintă un pointer (deci conţine o adresă şi anume adresa primului element de tablou).

- 24 -

Page 25: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Exemplu:Vom citi numele şi prenumele unei persoane şi le afişăm pe ecran.

#define MAX 20void main(void){ char nume[MAX+1], prenume[MAX+1]; //declararea tablourilor de caractere scanf (“%20s %20s”,nume, prenume); //nu s-a mai pus operatorul de adresa printf (“\nnumele: %s, prenumele: %s”,nume,prenume);}

4.3. FUNCŢIA STANDARD putchar Funcţia standard putchar se poate utiliza pentru a scrie un caracter în

fişierul standard de ieşire stdout, în poziţia curentă a cursorului. Ea se apelează folosind o instrucţiune de apel de forma:

putchar (expresie);

unde expresie este codul ASCII al caracterului care se scrie la ieşirea standard.

Practic putchar nu este o funcţie în sensul definiţiei ce am dat-o în lecţia 1, ci este un macrou definit în fişierul header stdio.h, care foloseşte o funcţie specială destinată prelucrării fişierelor, funcţia putc, astfel:

#define putchar(c) putc(c,stdout)Exemplu:void main (void){ putchar (‘A’); // scrie caracterul A in fisierul de iesire in poziia

curenta a cursorului putchar (‘A’+2) // scrie caracterul de cod ASCII ‘A’+2=65+2=67, adica

litera C putchar (‘\n’); // scrie caracterul newline; deci inceput de linie nouă}

4.4. FUNCŢIA STANDARD getchar Această funcţie citeşte de la intrarea standard, stdin, caracterul curent

şi returnează codul ASCII al caracterului citit. Tipul valorii returnate este int. La întâlnirea sfârşitului de fişier (CTRL/Z) se returnează valoarea EOF (adică -1). Ca şi putchar, getchar este un macrou definit în fişierul header stdio.h, cu ajutorul unei funcţii speciale, getc destinate prelucrării fişierelor, astfel:

#define getchar() getc(stdin)

De obicei getchar se foloseşte în expresii de atribuire de forma:c=getchar();După citirea caracterului curent de la intrarea standard se atribuie

variabilei c codul ASCII al caracterului cititi sau EOF la întâlnirea sfârşitului de fişier.

Exemple:1)#include <stdio.h>void main (void){ putchar(getchar() – ‘A’ + ‘a’); // citeste o litera mare si o rescrie

ca litera mica }

2) exemplul următor testează dacă s-a citit o literă mare şi numai în acest caz aplică transformarea ei în literă mică. În cazul în care la intrare nu se află o literă mare se rescrie caracterul respectiv.

#include <stdio.h>void main (void){ intc c; putchar(((c = getchar() )>= ‘A’ && c<= ‘Z’) ? c-‘A’+’a’ :c);}

- 25 -

Page 26: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

4.5. FUNCŢIILE STANDARD getche ŞI getch Funcţia getche citeşte de la intrarea standard caracterul curent şi

returnează codul ASCII al caracterului citit. Această funcţie are acces direct la caracter după ce a fost tastat şi are forma:

getche();

Funcţia getch este similară cu getche cu singura condiţie că citirea se face fără ecou (deci caracterul tastat nu se reafişează pe terminal). De fapt prezenţa/absenţa sufixului e precizează că funcţia e sau nu cu ecou. Cele două funcţii fac citiri fără intermedierea zonei tampon.

- 26 -

Page 27: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 5.I N S T R U C Ţ I U N I

5.1. SCURT ISTORIC AL METODELOR DE PROGRAMAREVom prezenta în continuare câteva metode de programare dar nu exhaustiv, nefiind

aici cadrul adecvat (eventual într-un curs de Software Engineering). O clasificare cronologică a ceea ce vrem să prezentăm ar fi următoarea:

programarea artizanală;programarea procedurală;programarea modulară;programarea structurată;programarea prin abstractizarea datelor;programarea orientată spre obiecte.

5.1.1. Programare artizanalăAceastă metodă de fapt nu este o metodă propriu-zisă ci este prima modalitate de

programare odată cu apariţia calculatoarelor. Intuiţia şi experienţa programatorului joacă un rol important. Fiecare programator îşi are propriile reguli de programare. Programele sunt monolitice (un singur corp de instrucţiuni), lungi şi greu de înţeles de alt programator. Însuşi cel ce a elaborat un astfel de program întâmpină dificultăţi de înţelegere a propriului program după un timp oarecare.

5.1.2. Programare proceduralăOdată cu apariţia primelor limbaje de înalt nivel se utilizează programarea

procedurală. Necesitatea ca anumite secvenţe de program să fie folosite de mai multe ori duce la organizarea acestora în unităţi distincte de program numite în diverse limbaje subprograme, subrutine, proceduri, etc. De multe ori procedurile trebuie să fie generale deci procesarea să facă abstractizare de valorile datelor. De exemplu o procedură de calcul al radicalului de ordinul 2 trebuie să calculeze acest lucru din orice număr real pozitiv iar pentru cele negative să semnaleze eroare. Procedurile trebuie deci parametrizate cu anumite variabile numite parametri formali. Valorile de la apel ale parametrilor formali se numesc parametri efectivi. Programarea procedurală are la bază deci utilizarea procedurilor, iar acestea realizează o abstractizare prin parametri. La apelare o procedură funcţionează după principiul cutiei negre (black box): se cunosc intrările şi ieşirile rezultate din acestea dar nu şi modul de transformare care nu e important în acest moment.

În majoritatea limbajelor procedurale de programare se consideră 2 categorii de proceduri:

proceduri care definesc o valoare de revenire (denumite şi funcţii);proceduri care nu definesc o valoare de revenire.În limbajele C şi C++ procedurile de ambele categorii se numesc funcţii.

5.1.3. Programarea modularăPe măsură ce complexitatea aplicaţiilor a crescut, a apărut ideea de a descompune

problemele în subprobleme mai simple care la rândul lor pot fi descompuse în altele mai simple şi aşa mai departe. În felul acesta se ajunge la o descompunere arborescentă a problemei date în subprobleme mai simple. Programarea subproblemelor devine o problemă mai simplă şi fiecare subproblemă are o anumită independenţă faţă de celelalte subprobleme. De asemenea, interfaţa ei cu celelalte subprobleme este limitată şi bine precizată prin procesul de descompunere a problemei iniţiale. De obicei, programarea unei subprobleme, componentă a descompunerii arborescente a problemei iniţiale, conduce la realizarea unui număr relativ mic de proceduri (funcţii). Aceste funcţii pot prelucra în comun anumite date. Unele dintre ele sunt independente de funcţiile realizate pentru alte subprobleme componente ale descompunerii arborescente. Altele realizează chiar interfaţa cu subproblemele învecinate.

Despre funcţiile obţinute în urma programării unei subprobleme se obişnuieşte să se spună că sunt înrudite. De obicei, aceste funcţii, împreună cu datele pe care le prelucrează, se păstrează într-un fişier şi se compilează independent.

O colecţie de funcţii înrudite, împreună cu datele pe care le prelucrează în comun formează un modul. În felul acesta, problema iniţială se realizează printr-un program alcătuit din module.

Programarea modulară are la bază elaborarea programelor pe module.O parte din datele utilizate în comun de funcţiile modulului, sau chiar toate

datele modulului, nu sunt necesare şi în alte module. Aceste date pot fi protejate sau cum se mai spune, ascunse în modul.

Limbajul C şi C++, permite ascunderea datelor în modul folosind date care au clasa de memorie static. Mai mult, pot fi declarate şi funcţiile ca statice şi atunci ele vor fi ascunse în modul (nu pot fi apelate din afara modului). Ascunderea funcţiilor în modul se face pentru acele funcţii care nu se utilizează la realizarea interfeţei modulului cu

- 27 -

Page 28: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

celelalte module. Ascunderea datelor şi funcţiilor în module permite protejarea datelor şi preîntâmpină utilizarea eronată a funcţiilor.

5.1.4. Programarea structuratăDescompunerea unei probleme în subprobleme mai simple se poate face succesiv în

mai multe etape, până când subproblemele sunt direct programabile sub forma unor proceduri sau module. Această descompunere succesivă se mai numeşte rafinare pas cu pas (stepwise refinement).. Evident că se obţine o descompunere arborescentă. Procedurile se pot organiza sau nu în module. În cadrul procedurilor se folosesc anumite structuri de control a execuţiei. Aceasta impune o anumită disciplină a programării. Structurile de control de sunt:

secvenţa;iteraţia (pretestată, posttestată, cu număr prestabilit de ciclări);alternativa (simplă, completă, generalizată).

Instrucţiunea de bază (primitivă) în cadrul acestor structuri de control este instrucţiunea de atribuire.

Această abordare a programării s-a născut din necesitatea eliminării instrucţiunii de control GO TO care face saltul necondiţionat la o instrucţiune precizată, alta decât instrucţiunea următoare ei. Profesorul Dijsktra de la Universitatea din Eindhoven spunea, prin anul 1965, “calitatea unui programator este invers proporţională cu numărul de instrucţiuni GO TO folosite” şi a impus D-structurile de control:

secvenţa;iteraţia pretestată;alternativa simplă.

D-structurile se regăsesc în toate limbajele procedurale. Corespondenţa ar fi:secvenţa este echivalentă cu execuţia instrucţiunilor în ordinea scrierii lor în

programul sursă;b) iteraţia pretestată echivalentă cu WHILE ... DO;c) alternativa simplă echivalentă cu IF ... THEN.

S-a demonstrat ulterior (Bohm şi Jacopini) că orice algoritm se poate descrie doar cu D-structurile dar pentru o mai bună lizibilitate şi înţelegere a programelor sursă s-au adăugat şi iteraţia postestată (REPEAT ... UNTIL), iteraţia cu număr prestabilit de ciclări (FOR ... DO), alternativa completă (IF ... THEN ... ELSE) şi alternativa generalizată (CASE ... OF).

În unele limbaje se folosesc şi alte structuri pe lângă cele de mai sus pentru o cât mai fidelă reflectare a algoritmului.

5.1.5. Programarea prin abstractizarea datelorÎn toate aceste tehnologii anterioare se urmăreşte mai mult organizarea

programului şi mai puţin rezolvarea cât mai naturală a problemei. Programarea prin abstractizarea datelor şi programarea orientată spre obiecte propun metodologii în care conceptele deduse din analiza problemei să poată fi reflectate cât mai fidel în program şi să se poată manevra cu instanţieri ale acestor concepte cât mai natural. Se realizează o mai mare fidelitate a programului faţă de problemă. De exemplu dacă într-o problemă în care se procesează numere complexe e nevoie să se lucreze într-o formă cât mai apropiată cu forma matematică se poate introduce tipul COMPLEX (tip care nu există în limbajele de programare) şi apoi se pot declara variabile de acest tip. Mai mult ar trebui să se poată face toate operaţiile matematice asupra datelor de tip COMPLEX. În general un TAD (Tip Abstract de Date) are două componente fundamentale:

datele membru (reflectă reprezentarea tipului);funcţiile membru (reflectă comportamentul tipului).

5.1.6. Programarea orientată spre obiecteUn neajuns al programării prin abstractizarea datelor este faptul că nu permite

exprimarea legăturilor dintre diferite concepte (TAD-uri). Singura legătură dintre concepte care se poate exprima, este aceea că datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest lucru nu este suficient în cazul în care conceptele sunt strâns dependente între ele formând structuri ierarhice. Exprimarea ierarhiilor conduce la atribute suplimentare cum sunt cele de moştenire. Aceste atribute conduc la un nou model de programare pe care îl numim programare orientată obiect.

În vârful unei ierarhii se află fenomenul sau forma de existenţă care are trăsături comune pentru toate celelalte componente ale ierarhiei respective. Pe nivelul următor al ierarhiei se află componentele care pe lângă trăsăturile comune de pe nivelul superior, mai au şi trăsături suplimentare, specifice. O ierarhie, de obicei, are mai multe nivele, iar situarea unui element pe un nivel sau altul al ierarhiei este uneori o problemă deosebit de complexă. Dăm în exemplul următor o ierarhie arborescentă pentru conceptele de paralelogram, dreptunghi, romb şi pătrat.

- 28 -

Page 29: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Dacă paralelogramul se află în vârful ierarhiei atunci pe nivelul imediat inferior se aşează dreptunghiul (paralelogramul cu un unghi drept) dar şi rombul (paralelgramul cu 2 laturi alăturate congruente). Apoi pătratul se poate defini fie ca un dreptunghi cu laturile congruente fie ca un romb cu un unghi drept. Conceptul de pe fiecare nivel se observă că moşteneşte proprietăţile conceptului imediat superior din care este derivat.

La ora actuală, toate ramurile cunoaşterii ştiinţfice sunt pline de ierarhii rezultate în urma clasificării cunoştinţelor acumulate în perioada lungă de observare a fenomenelor şi formelor de existenţă a lumii materiale şi spirituale. Clasificările ierarhice ale cunoştinţelor pot fi întâlnite atât în domeniile care pleacă de la cele mai concrete forme ale lumii materiale, cum sunt botanica, zoologia, biologia, etc cât şi în domenii care studiază concepte dintre cele mai abstracte, cum ar fi matematica sau filozofia.

Aceste ierarhii sunt rezultatul definirii conceptelor după regula includerii “genul proxim şi diferenţa specifică”.

Limbajul C dispune de un set bogat de instrucţiuni care permit scrierea de:programe structurate, programe flexibile, programe compacte.

Totodată limbajul C permite aplicarea metodelor de programare procedurală, programare modulară şi programare structurată. Pe lângă aceste metodologii limbajul C++ permite şi programarea prin abstractizarea datelor şi programarea orientată spre obiecte.

Vom descrie în continuare instrucţiunile limbajului C. Ca o caracteristică sintactică toate instrucţiunile limbajului se termină prin caracterul “;”, excepţie făcând instrucţiunile care se termină cu acolada închisă.

Limbajul C dispune de un set bogat de instrucţiuni care permit scrierea de:programe structurate, programe flexibile, programe compacte.

Totodată limbajul C permite aplicarea metodelor de programare procedurală, programare modulară şi programare structurată. Pe lângă aceste metodologii limbajul C++ permite şi programarea prin abstractizarea datelor şi programarea orientată spre obiecte.

Vom descrie în continuare instrucţiunile limbajului C. Ca o caracteristică sintactică toate instrucţiunile limbajului se termină prin caracterul “;”, excepţie făcând instrucţiunile care se termină cu acolada închisă.

5.2. INSTRUCŢIUNEA VIDĂInstrucţiunea vidă se reduce la caracterul “;”. Ea nu are nici un efect.

Adesea este nevoie de ea la construcţii în care se cere prezenţa unei instrucţiuni, dar nu este necesar să se execute nimic în punctul respectiv.

5.3. INSTRUCŢIUNEA EXPRESIEInstrucţiunea expresie se obţine scriind punct şi virgulă după o expresie,

deci:expresie;

Există cazuri particulare ale instrucţiunii expresie:expresia de atribuire, care de altfel este cel mai important caz

particular al instrucţiunii expresie:expresie;apelul unei funcţii:nume_funcţie (par1, par2, . . . parn);incrementările şi decrementările pre şi post fixate:variabilă++; ++variabilă;variabilă- -; - - variabilă;Exemplu:void main (void){ int i; float f; double d; i=10; // instructiune de atribuire i++; // i se mareste cu 1 f=i; // instructiune de atribuire (valoarea lui i se converteste in

float) d=++f; // incrementare lui f si atribuirea valorii lui d putchar(‘a’); // instructiune de apel }

- 29 -

Page 30: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

5.4. INSTRUCŢIUNEA COMPUSĂInstrucţiunea compusă este o succesiune de declaraţii urmate de

instrucţiuni, succesiune inclusă între acolade:{ declaraţii

instrucţiuni}

Pot lipsi declaraţiile sau instrucţiunle dar nu în acelaşi timp. Dacă delaraţiile sunt prezente, atunci ele definesc variabile care sunt valabile numai în instrucţiunea compusă respectivă.

Exemplu:. . . { int i=100;// variabila i este definita in aceasta instructiune compusa i++; // i are valabilitate doar intre acolade; dupa acolada inchisa i isi printf (“i=%d\n”,i); // pierde valabilitatea}

Observaţii:1o. După acolada inchisă a unei instrucţiuni compuse nu se pune ”;”.2o. Corpul unei funcţii are aceeaşi structură ca şi instrucţiunea compusă,

deci o funcţie are formatul:antetul funcţieiinstrucţiune compusă

5.5. INSTRUCŢIUNEA if Instrucţiunea if permite să realizăm o ramificare a execuţiei în funcţie

de valoarea unei expresii. Ea are două formate ce permit aplicarea structurii de alternativă simplă şi compusă.

Formatul 1:if (expresie) instructiune;Efectul:se evaluează expresia din paranteze;dacă valoarea expresiei este diferită de zero (deci conform convenţiei are

valoarea adevărat), atunci se execută instructiune, altfel se trece la instrucţiunea următoare.

Formatul 2:if (expresie) instructiune_1;

else instructiune_2;Efectul:se evaluează expresia din paranteze;dacă valoarea expresiei este diferită de zero (deci conform convenţiei are

valoarea adevărat), atunci se execută instructiune_1, altfel se execută instructiune_2; apoi în ambele cazuri se trece la instrucţiunea următoare.

Observaţii:1o. Se pot folosi instrucţiuni if imbricate, nivelul de imbricare fiind

oarecare (deci nu există o limitare a numărului de imbricări).2o. Pentru mai multe imbricări se foloseşte regula asocierii if-lui cu

else astfel:un else se pune în corespondenţă cu primul if care se află înaintea lui în

textul sursă şi nu este inclus în instrucţiunea care îl precede pe el şi nici nu îi corespunde deja un else.

Exemplevoid main (void){ float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if iar if (y<0) a=1; // penultimul else se asociaza cu cel de-al doilea if

else a=2; else a=0;}

- 30 -

Page 31: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

void main (void){ float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if deoarece cel de-al { if (y<0) a=1; } // de-al doilea if este inclus in

instructiunea compusa care else a=0; // il precede pe if}void main (void) // citeste trei intregi si scrie minimul dintre ei{int i,j,k,min; scanf (“\ndati i=%d”, &i); scanf (“\ndati j=%d”, &j); scanf (“\ndati k=%d”, &k); if (i>j) min=j; else min=i; if (k<min) min=k; printf (“min(%d,%d,%d)= %d\n”,i,j,k,,min);}

5.6. INSTRUCŢIUNEA while

Instrucţiunea while are următorul format:

while (expresie) instructiune;

Cu ajutorul instrucţiunii while se realizează structura repetitivă pretestată (condiţionată anterior).

Efectul:se evaluează valoarea expresiei din paranteze;dacă expresia are valoarea diferită de zero, atunci se execută

instructiune şi se reia punctul 1), altfel se trece la instrucţiunea următoare instrucţiunii while.

Deci instructiune se execută repetat atâta timp cât expresia din paranteză este diferită de zero. Se observă că dacă expresia are valoarea zero de la început, atunci instructiune nu se execută niciodată.

Antetul ciclului while este construcţia while (expresie) iar instructiune formează corpul ciclului. În cazul în care este necesar să se execute repetat mai multe instrucţiuni, se utilizează o instrucţiune compusă formată din instrucţiunile respective.

Exemplu:Vom crea un program care citeşte un întreg n şi scrie n!. Algoritmul în

pseudocod este următorul:

Citeste nf=1i=2CâtTimp i<=n

execută f=f*i;

i=i+1

SfârşitCâtTimpScrie n,f

Programul în C este:

#include<stdio.h>void main (void){ int n,i; double f; f=1.0; i=2; printf(“\n dati n= “); scanf(“%d”,&n); while (i<=n)

{ f=f*i; i++;}

printf(“\nn=%d, iar n!=%g\n”,n,f);

}Corpul ciclului while se poate scrie mai compact astfel:

while (i<=n) f*=i++;

- 31 -

Page 32: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

5.7. INSTRUCŢIUNEA for

Instrucţiunea for, ca şi instrucţiunea while, se utilizează pentru a realiza o structură repetitivă pretestată. Formatul instrucţiunii este:

for(exp1; exp2; exp3) instructiune;

Antetul ciclului este definit de for(exp1; exp2; exp3) iar instructiune formează corpul ciclului. Prima expresie exp1 constituie partea de iniţializare a ciclului, iar exp3 este partea de reiniţializare a ciclului. Condiţia de continuare a ciclului este exp2. De obicei exp1 şi exp3 reprezintă atribuiri.

Efectul:se execută secvenţa de iniţializare definită de expresia exp1;se evaluează exp2; dacă exp2 are valoarea zero, atunci se iese din ciclu,

adică se trece la instrucţiunea următoare instrucţiunii for, altfel se execută instrucţiunea din corpul ciclului;

se execută apoi secvenţa de reiniţializare definită de exp3, apoi se reia secvenţa de la punctul 2).

Observaţii:1o. Ca şi în cazul instrucţiunii while, instrucţiunea din corpul ciclului

for poate să nu se execute niciodată dacă exp2 are valoarea zero chiar la prima evaluare.

2o. Expresiile din antetul instrucţiunii for pot fi şi vide; totuşi caracterele “;” vor fi întotdeauna prezente.

3o. Comparând instrucţiunile for şi while observăm că instrucţiunea for cu formatul anterior se poate realiza cu secvenţa următoare folosind while:

exp1;while (exp2)

{ instructiune; exp3;

}Invers, o instrucţiune while de forma: while (exp) instructiune este

echivalentă cu următoarea instrucţiune for:for(; exp; ) instructiune.

Autorii limbajului C propun ca instrucţiunea for să se folosească cu prioritate pentru ciclurile care au pas.

Exemple:Vom da o secvenţă de instrucţiuni care însumează elementele unui tablou:s=0;for(i=0; i<n; i++) s=s+tab[i];sau scrisă mai compact:for (s=0, i=0; i<n; i++) s+=tab[i];În continuare vom da un mic program care afişează numărul caracterelor

citite de la intrarea standard stdin.

#include <stdio.h>void main(void){ long n; for (n=0; getchar()!=EOF; n++); printf (“\nnumarul caracterelor citite =%ld\n”,n);}sau scrisă cu instrucţiunea while

#include <stdio.h>void main(void){ long n=0; while (getchar()!=EOF) n++; printf (“\nnumarul caracterelor citite =%ld\n”,n);}

- 32 -

Page 33: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

5.8. INSTRUCŢIUNEA do-while

Această instrucţiune realizează structura repetitivă condiţionată posterior (posttestată) dar modificată faţă de REPEAT .. UNTIL. Această instrucţiune s-a introdus pentru o mai mare flexibilitate în scrierea programelor. Formatul ei este:

do instructiune;while (exp);

Efectul:se execută instrucţiunea instructiune;se evaluează expresia exp din paranteze;dacă valoarea expresiei este zero se trece la instrucţiunea următoare

instrucţiunii do-while; altfel se revine şi se execută din nou instructiune.

Observaţii:1o. Structura realizată de instrucţiunea do-while poate fi realizată

printr-o secvenţă în care se foloseşte instrucţiunea while astfel:instructiune;while (exp) instructiune; 2o. Se observă că în cazul instrucţiunii do-while, corpul ciclului se

execută cel puţin odată, spre deosebire de ciclurile while şi for unde corpul ciclului poate să nu se execute niciodată.

Exemplu:Vom da un program care calculează rădăcina pătrată dintr-un număr real

a>=0.

#include<stdio.h>#define EPS 1e-10void main (void){ double x1,x2,y,a; clrscr(); // sterge ecranul printf(“\ndati un numar real pozitiv a=”); if (scanf(“%lf”,&a) !=1 || a<0) printf (“numarul citit nu este

pozitiv\n”); else {

x2 = 1.0; do { x1 = x2;

x2 = 0.5 *(x1+a/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS); printf (“radacina patrata din:%g este: %.2lf\n”,a,x2); // 2 zecimale } //sfirsit else }

5.9. INSTRUCTIUNEA switch

Instrucţiunea switch permite realizarea structurii alternativa generalizată. Ea este echivalentă cu o imbricare de structuri de alternativă simple. Utilizarea instrucţiunii switch face în schimb programul mult mai clar.

Formatul instrucţiunii switch este următorul:switch (exp){ case c1: sir1

break; case c2: sir2

break; . . . case cn: sirn

break; default: sir}

unde: c1,. . . cn sunt constante sau constante simbolice; sir1, . . . ,sirn, sir sunt şiruri de instrucţiuni.

- 33 -

Page 34: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Efectul:se evaluează expresia din paranteză;se compară pe rând valoarea expresiei cu valorile constantelor c1, . . . ,

cn;dacă valoarea expresiei coincide cu valoarea lui ck, se execută secvenţa

de instrucţiuni definită prin sirk; în cazul în care valoarea expresiei nu coincide cu nici una din constantele c1, . . . , cn, se execută secvenţa de instrucţiuni definită prin sir;

după execuţia secvenţei sirk sau sir se trece la instrucţiunea următoare instrucţiunii switch, adică la prima instrucţiune aflată după acolada închisă care termină instrucţiunea switch respectivă; evident, acest lucru are loc dacă şirul care se execută nu impune, el insuşi, un alt mod de continuare a execuţiei, de exemplu o revenire din funcţia respectivă, un salt la o anumită instrucţiune, etc.

Observaţii:1o. Ramura default nu este obligatorie. În lipsa ei, dacă valoarea

expresiei nu coincide cu nici una din constantele c1,. . . , cn, instrucţiunea switch respectivă nu are nici un efect.

2o.Construcţia break reprezintă o instrucţiune. Ea termină fiecare ramură de instrucţiuni sir1, . . . , sirn, provocând saltul la instrucţiunea următoare instrucţiunii switch sau, cum se mai spune, realizează ieşirea din instrucţiunea switch.

3o. Instrucţiunea break nu este obligatorie. În cazul în care este absentă, se execută secvenţial următoarea ramură. De exemplu dacă avem secvenţa:

switch (exp){ case c1: sir1

case c2: sir2

}ea se execută în felul următor:dacă valoarea expresiei este egală cu c1 se execută sir1 şi apoi sir2;dacă valoarea expresiei este egală cu c2 se execută sir2;daca valoarea expresiei difera de valorile c1 şi c2 instrucţiunea switch de

mai sus nu este efectivă, se trece la instrucţiunea următoare care urmează după switch.

secvenţa de mai sus se putea realiza şi astfel:if (exp = = c1){ sir1 sir2}else if (exp = = c2) sir2

Exemplu:Vom citi din fişierul de intrare construcţii de forma: op1 operator op2,

unde op1 şi op2 sunt numere întregi (operanzi întregi) iar operator este un operator aritmetic {“+”, “-“, “*”, “/”}. La ieşire se va scrie valoarea expresiei citite. De exemplu dacă se citeşte secvenţa 100/3 se va afişa rezultatul 33. Programul permite citirea şi evaluarea mai multor astfel de expresii, până la întâlnirea sfârşitului de fişier.

#include <stdio.h>void main (void){ int op1,op2,operator,rezultat,i; while (( i=scanf(“%d %c %d”, &op1,&operator, &op2)) != EOF) if (i = = 3 ) // ramura adevarat inseamna ca s-au citit 3

campuri corecte { switch (operator)

{ case ‘+’: rezultat = op1 + op2 ; // avem adunare break;

case ‘-‘ : rezultat = op1 – op2; // avem scadere break;

case ‘*’ : rezultat = op1 * op2; // avem inmultire break;

case ‘/’ : // avem impartire intreaga if (op2 = = 0)

{ printf (“divizor nul\n”); rezultat = 0;

- 34 -

Page 35: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

} else rezultat = op1 / op2; break;

default : printf (“operator eronat\n”); rezultat = 0;

} // sfarsit switch printf (“%d %c %d %d\n”, op1, operator, op2, rezultat); } else

printf (“expresie eronat\n”); // sfarsit if si while

}

5.10. INSTRUCŢIUNEA break

Formatul instrucţiunii este următorul:break;

De obicei instrucţiunea break se foloseşte pentru a ieşi dintr-un ciclu. Dacă există mai multe cicluri imbricate instrucţiunea break va trece controlul la ciclul de nivel imediat superior (deci imbricarea rămâne, nu se iese din toate ciclurile). O altă utilizare este în instrucţiunea switch, după cum am observat în paragraful anterior.

Un alt exemplu de utilizare frecventă este ieşirea dintr-un ciclu infinit de forma:

for ( ; ; ){. . . if (exp) break; . . .}

5.11. INSTRUCŢIUNEA continue

Formatul instrucţiunii este următorul:continue;Efectul:în ciclurile while şi do-while ea realizează saltul la evaluarea expresiei

care decide asupra continuării ciclului;în ciclul for ea realizează saltul la pasul de reiniţializare.Observaţie:1o. Instrucţiunea continue se utilizează numai în corpul unui ciclu,

permiţând, după caz, să se treacă la pasul următor al ciclului sau să se iasă din ciclu.

5.12. INSTRUCŢIUNEA goto

Conform principiilor programării structurate instrucţiunea goto nu ar fi necesară. Dar ea a fost introdusă în limbaj, deoarece, în anumite cazuri, se dovedeşte a fi utilă, asigurând o flexibilitate mai mare în programare. De multe ori ieşirea dintr-un ciclu imbricat în alte cicluri se realizează mai simplu cu ajutorul instrucţiunii goto. În lipsa ei ar trebui să folosim mai mulţi indicatori şi teste asupra valorilor acestora pentru ieşirea din ciclu. Saltul făcut de goto se face la o instrucţiune care este prefixată de o etichetă.

Prin etichetă vom înţelege un nume urmat de caracterul “:”. Etichetele sunt locale unei funcţii.

Instrucţiunea goto are următorul format:goto eticheta;

Efectul:se realizează saltul la instrucţiunea prefixată de eticheta al cărei nume

se află scris după cuvântul cheie goto.

- 35 -

Page 36: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

5.13. APELUL ŞI REVENIREA DINTR-O FUNCŢIE5.13.1. Apelul unei funcţiiÎn limbajul C funcţiile sunt de două tipuri:funcţii care returnează o valoare la revenirea din ele;funcţii care nu returnează nici o valoare la revenirea din ele.

O funcţie care nu returnează nici o valoare la revenirea din ea se apelează printr-o instrucţiune de apel. Ea are următorul format:

nume (lista_parametrilor_efectivi); (*)unde:nume este numele funcţiei;lista_parametrilor_efectivi este fie vidă, fie se compune din una sau mai

multe expresii separate prin virgulă.Instrucţiunea de apel este un caz particular al instrucţiunii expresie.

Parametrii efectivi (de la apel) trebuie să corespundă cu cei formali (de la definirea funcţiei) prin ordine, tip şi număr.

În cazul în care o funcţie returnează o valoare, ea poate fi apelată fie printr-o instrucţiune de apel, fie sub forma unui operand al unei expresii.

Observaţii:1o. Dacă nu dorim să utilizăm valoarea returnată de funcţia respectivă,

apelul se face printr-o instrucţiune de apel.2o. Dacă dorim să utilizăm valoarea returnată de funcţie, vom folosi

apelul funcţiei drept operand într-o expresie, operandul având formatul (*).Exemple de apeluri de funcţii folosite până acum sunt apelurile funcţiilor

standard printf, scanf, getchar şi putchar. Funcţiile printf şi putchar au fost apelate prin instrucţiuni de apel, valorile returnate de ele nefiind utilizate. În schimb funcţiile scanf şi getchar au fost apelate în ambele moduri, atât prin instrucţiuni de apel, cât şi ca operanzi în diferite expresii.

5.13.2. Prototipul unei funcţiiO funcţie poate fi apelată dacă ea este definită în fişierul sursă înainte

de a fi apelată. După cum am prezentat în lecţia 1 nu întotdeauna este posibil acest lucru şi în astfel de cazuri apelul funcţiei trebuie să fie precedat de prototipul ei.

Prototipul unei funcţii are ca scop să informeze compilatorul despre:tipul valorii returnate de funcţie;tipurile parametrilor.

În felul acesta, la apelul unei funcţii, compilatorul poate face teste cu privire la tipul expresiilor care reprezintă parametrii efectivi, precum şi unele conversii necesare asupra valorii returnate de funcţie.

Observaţii:1o. Tipurile parametrilor pot să lipsească. În acest caz, compilatorul nu

controlează tipurile parametrilor efectivi, singura informaţie conţinută de prototip fiind tipul valorii returnate de funcţia respectivă.

2o. Absenţa atât a prototipului unei funcţii, cât şi a definiţiei funcţiei înainte de a fi apelată este posibilă; în acest caz se presupune că funcţia returnează o valoare de tip int.

3o. În practică se recomandă utilizarea prototipurilor pentru toate funcţiile înainte de a fi apelate. În acest scop, ele vor fi scrise la începutul fişierelor sursă.

Formatele posibile ale unui prototip sunt:

formatul 1: tip nume (lista_declaratiilor_de_parametri);formatul 2: tip nume (lista_ tipurilor_parametrilor);formatul 3: tip nume (void);formatul 4: tip nume ();Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi pentru orice

funcţie care nu are parametri. Formatul 4 se poate folosi pentru orice funcţie la al cărei apel nu se doresc teste referitoare la tipul parametrilor efectivi.

Funcţiile din biblioteca standard a limbajului C au prototipurile definite în fişierele de tip .h.

- 36 -

Page 37: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

5.13.3. Apel prin valoare şi apel prin referinţăLa apelul unei funcţii, fiecărui parametru formal i se atribuie valoarea

parametrului efectiv care-i corespunde. Deci, la apelul unei funcţii se transferă valorile parametrilor efectivi. Din această cauză se spune că apelul este prin valoare (call by value). În anumite limbaje de programare, la apel nu se transferă valorile parametrilor efectivi ci adresele acestora. În acest caz se spune că apelul este prin referinţă (call by refference).

Între cele două tipuri de apeluri există o diferenţă esenţială şi anume: în cazul apelului prin valoare funcţia apelată nu poate modifica parametrii efectivi din funcţia apelantă, neavând acces la ei. În cazul apelului prin referinţă, funcţia apelată, dispunând de adresele parametrilor efectivi, îi poate modifica.

În limbajul C apelul se realizează implicit prin valoare. În cazul că un parametru este numele unui tablou atunci transferul se realizează prin referinţă deoarece numele unui tablou este un pointer şi conţine adresa primului element al tabloului. Transferul prin referinţă se realizează cu ajutorul variabilelor de tip pointer şi cu ajutorul operatorului de adresă (&).

5.13.4. Revenirea dintr-o funcţieRevenirea dintr-o funcţie se poate face în două moduri:la întâlnirea instrucţiunii return;după execuţia ultimei sale instrucţiuni, adică a instrucţiunii care

precede acolada închisă ce termină corpul funcţiei respective.Instrucţiunea return are două formate:return; sau return expresie;

Primul format se utilizează când funcţia nu returnează o valoare, iar cel de-al doilea când funcţia returnează o valoare. În acest ultim caz, funcţia returnează valoarea expresiei specificate.

Observaţie:1o. Când revenirea se face după execuţia ultimei instrucţiuni a funcţiei

nu se returnează o valoare; revenirea în acest caz, se face ca şi cum acolada închisă de la sfârşitul corpului funcţiei ar fi precedată de instrucţiunea return.

Exemplu: vom da un exemplu de apel al funcţiei care determină rădacina pătratică dintr-un număr nenegativ.

#include<stdio.h>double radacina_2 (double) // prototipul functiei

void main (void) // functia principala care citeste d // si afiseaza radacina patrata din d{ double d; clrscr(); // sterge ecranul if (scanf (“%lf”,&d) != || d<0)

printf (“numarul dat este eronat\n”); elseprintf (“d=%f, radacina patrata = %.10g\n”, d, radacina_2(d));#define EPS 1e-10double radacina_2 (double x){ double x1,x2,y; x2 = 1.0; do { x1 = x2; x2 = 0.5 *(x1+x/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS); return x2;}Observaţie:1o. Limbajul C dispune de o bibliotecă matematică în care sunt incluse o

serie de funcţii pentru calculul valorilor funcţiilor elementare. Există o funcţie numită sqrt (cu prototipul double sqrt (double);). Fişierul care conţine biblioteca matematică se numeşte math.h şi trebuie inclus în fişierul sursă de lucru dacă se doreşte utilizarea funcţiilor definite în el.

- 37 -

Page 38: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- 38 -

Page 39: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 6.P O I N T E R I

Un pointer este o variabilă care are ca valori adrese. Pointerii se folosesc pentru a face referire la date cunoscute prin adresele lor. Astfel, dacă p este o variabilă de tip pointer care are ca valoare adresa zonei de memorie alocată pentru variabila întreagă x atunci construcţia *p reprezintă chiar valoarea variabilei x.

În construcţia de mai sus, *p, caracterul * se consideră ca fiind un operator unar care furnizează valoarea din zona de memorie a cărei adresă este conţinută în p. Operatorul unar * are aceeaşi prioritate ca şi ceilalţi operatori unari din limbajul C.

Dacă p conţine adresa zonei de memorie alocată variabilei x, vom spune că p pointează spre x sau că p conţine adresa lui x.

Pentru a atribui unui pointer adresa unei variabile, putem folosi operatorul unar &. Astfel, dacă dorim ca p să pointeze spre x, putem utiliza construcţia:

p = &x;În limba română se utilizează şi alte denumiri pentru noţiunea de

pointer: referinţă, localizator; reper; indicator de adresă.

6.1. DECLARAŢIA DE POINTERUn pointer se declară ca orice variabilă cu deosebirea că numele

pointerului este precedat de caracterul *. Astfel, dacă, de exemplu, dorim să declarăm variabila p utilizată anterior pentru a păstra adresa variabilei întregi x, vom folosi declaraţia următoare:

int *p;

Tipul int stabileşte în acest caz faptul că p conţine adrese de zone de memorie alocate datelor de tip int. Declaraţia lui p se poate interpreta în felul următor: *p reprezintă conţinutul zonei de memorie spre care pointează p, iar acest conţinut are tipul int.

În general, un pointer se declară prin:tip *nume;

ceea ce înseamnă că nume este un pointer care pointează spre o zonă de memorie ce conţine o dată de tipul tip.

Comparând declaraţia de pointer anterioară cu una obişnuită: tip nume;putem considera că:tip *dintr-o declaraţie de pointer reprezintă tip dintr-o declaraţie obişnuită.

De aceea, construcţia tip *se spune că reprezintă un tip nou, tipul pointer.

Există cazuri în care dorim ca un pointer să fie utilizat cu mai multe tipuri de date. În acest caz, la declararea lui nu dorim să specificăm un tip anume. Aceasta se realizează folosind cuvântul cheie void:

void *nume;Exemple:1)void main (void){ int x,y; int *p; y=x+10; // aceast atribuire este echivalenta cu secventa urmatoare p=&x; y=*p+100;

x=y; // este echivalenta cu secventa p=&x; (*p)++;}

- 39 -

Page 40: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

2) funcţia permutare de mai jos realizează transferul parametrilor prin adresă:

void permutare (int *x, int *y) // x si y sunt pointeri{ int temp; temp = *x; // temp ia valoarea ce se afla la adresa continuta in x *x=*y; // in zona a carei adresa se afla in x se

transfera continutul// zonei a carei adresa se afla in y

*y=temp; // in zona a carei adresa se afla in y se transfera valoarea // lui temp}

Apelul funcţiei permutare se face astfel:

permutare (&a, &b);

pentru a schimba valorile lui a cu b.

6.2. LEGĂTURA DINTRE POINTERI ŞI TABLOURINumele unui tablou este un pointer deoarece el are ca valoare adresa

primului său element. Totuşi există o diferenţă între numele unui tablou şi o variabilă de tip pointer, şi anume unui nume de tablou nu i se poate atribui altă adresă. Deci numele unui tablou trebuie considerat ca fiind un pointer constant.

Dacă x este un parametru formal ce corespunde unui parametru efectiv care este un nume de tablou, x poate fi declarat fie ca tablou fie ca pointer spre tipul tabloului.

Exemplu:Fie funcţia cu antetul următor:unsigned lungime (char x[ ]);Să presupunem că această funcţie determină lungimea unui şir de caractere

şi se poate apela prin:l=lungime(tablou);unde tablou este de tip caracter.Antetul funcţiei lungime poate fi schimbat în felul următor:unsigned lungime (char *x);

Cele două declaraţii sunt identice deoarece declaraţia:char x[ ];

defineşte pe x ca numele unui tablou de tip caracter; dar atunci el este un pointer spre caractere deci se poate declara prin:

char *x;

6.3. OPERAŢII CU POINTERIAsupra pointerilor se pot face diferite operaţii. Deoarece ei conţin

adrese atunci operaţiile se realizează cu adrese.

6.3.1. Incrementare şi decrementareOperatorii de incrementare şi decrementare se pot aplica

variabilelor de tip pointer.Efectul:operatorul de incrementare aplicat asupra unui operand de tip pointer spre

tipul tip măreşte adresa conţinută de operand cu numărul de octeţi necesari pentru a păstra o dată de tipul tip.

operatorul de decrementare se execută în mod analog, cu singura diferenţă că în loc să se mărească adresa, ea se micşorează cu numărul corespunzător de octeţi.

De obicei decrementările şi incrementările adreselor sunt mai rapide ca execuţie când se au în vedere prelucrări de tablouri.

- 40 -

Page 41: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Exemplu:int tab[10];int *p;int i=0;p=&tab[i];p++; // p contine adresa lui tab[1]

// cu p se pot face referiri la orice element de tablou

6.3.2. Adunarea şi scăderea unui întreg dintr-un pointerDacă p este un pointer, sunt corecte expresiile de forma: p+n şi p-nunde n este de tip întreg.

Efectul:expresia p+n măreşte valoarea lui p cu n*nr_tip, unde nr_tip este numărul

de octeţi necesari pentru a memora o dată de tipul tip spre care pointează p;analog expresia p-n micşorează valoarea lui p cu n*nr_tip.

Dacă x este un tablou de tipul tip, atunci x este pointer, deci o expresie de forma:

x+n;

este corectă şi deoarece x este un pointer spre primul său element x[0], x+n va fi un pointer spre elementul x[n]. Rezultă că valoarea elementului x[n] se poate reprezenta prin expresia:

*(x+n);

Astfel variabilele cu indici se pot înlocui prin expresii cu pointeri. Aceasta permite ca la tratarea tablourilor să se folosească expresii cu pointeri în locul variabilelor cu indici. Versiunile cu pointeri sunt de obicei optime în raport cu cele realizate prin intermediul indicilor.

6.3.3. Compararea a doi pointeriDoi pointeri care pointează spre elementele aceluiaşi tablou pot fi

comparaţi folosind operatorii de relaţie şi de egalitate. Astfel, dacă p şi q sunt doi pointeri care pointează spre elementele tab[i], respectiv tab[j] ale tabloului tab, expresiile următoare au sens:

p<q p!=j p= =q.

Observaţii:1o. Pointerii nu pot fi comparaţi decât în condiţiile amintite mai sus

(deci dacă sunt pointeri spre elementele aceluiaşi tablou).2o. Operatorii = = şi != permit compararea unui pointer şi cu o constantă

simbolică specială având numele NULL. Aceste comparaţii permit să stabilim dacă un pointer conţine o adresă sau nu. Astfel, dacă expresia:

p= = NULL

este adevărată, p nu conţine o adresă. Dacă expresia respectivă are valoarea fals atunci p conţine o adresă. Constanta simbolică NULL este definită în fişierul stdio.h

.6.3.4. Diferenţa a doi pointeriDoi pointeri care pointează spre elementele aceluiaşi tablou pot fi

scăzuţi. Rezultatul diferenţei a doi pointeri este definit astfel: fie t un tablou de un tip oarecare şi p şi q doi pointeri, p conţine adresa elementului t[i] iar q conţine adresa elementului t[i+n]. Atunci diferenţa q-p are valoarea n.

6.3.5. ExempleVom da câteva funcţii asupra şirurilor de caractere: funcţia lungime

unsigned lungime (char*x){ int i; for (i=0; *x != ‘\0’; i++) x++; // sau for (i=0; *x++; i++);

- 41 -

Page 42: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

return i;}

funcţia copiazăvoid copiaza(char *x, char *y)// copiaza din zona de adresa y

// in zona de adresa x{ while(*x++ = = *y++); }

funcţia concateneazavoid concateneaza (char *x, char *y)

// concateneaza sirul de adresa y la sfarsitul sirului // de adresa x { while (*x) x++; // avans de adresa pana la sfarsitul

sirului x while (*x++= *y++);

}funcţia compara

int compara (char *x, char *y){ while (*x= = *y)

{ if (*x= = ‘\0’) return 0; x++; y++;

return *x - *y; // daca diferenta caracterelor este // negativa atunci x<y altfel x>y}}

6.4. ALOCAREA DINAMICĂ A MEMORIEIBiblioteca standard a limbajului C pune la dispoziţia utilizatorului

funcţii care permit alocarea de zone de memorie în timpul execuţiei programului. O astfel de zonă de memorie poate fi utilizată pentru a păstra date temporare. Zona respectivă poate fi eliberată în momentul în care nu mai sunt necesare datele care au fost păstrate în ea. Alocarea de zone de memorie şi eliberarea lor în timpul execuţiei programelor permite gestionarea optimă a memoriei de către programator. Un astfel de mijloc de gestionare a memoriei îl vom numi alocare dinamică a memoriei.

Vom indica două funcţii din bibloteca limbajului C utilizate frecvent în alocarea dinamică a memoriei. Prototipurile lor se află în fişierele standard alloc.h şi stdlib.h, deci pentru a le utiliza vom include unul din aceste fişiere.

Funcţia malloc permite alocarea unui bloc de memorie a cărui dimensiune se specifică în octeţi. Funcţia returnează un pointer spre începutul zonei alocate. Întrucât acest pointer trebuie să permită memorarea oricărui tip de dată în zona alocată, el este de tip void *.

Prototipul funcţiei este:void *malloc (unsigned n);unde n este numărul de octeţi al zonei de memorie care se alocă. În cazul

în care n este prea mare, funcţia returnează pointerul NULL.Funcţia free eliberează o zonă de memorie alocată prin malloc.

Prototipul ei este:void free (void *p);unde p este pointerul returnat de malloc la alocare, deci este pointerul

spre începutul zonei care se eliberează.Exemplu:Funcţia memchar memorează un şir de caractere într-o zonă de memorie

alocată prin funcţia malloc. Ea returnează adresa de început a zonei în care s-a salvat şirul de caractere, deci returnează un pointer spre tipul char.

#include <stdio.h>#include <alloc.h>#include <string.h>char *memchar (char *s){ char *p;

if ((p=(char *)malloc(strlen(s)+1) ) != NULL{ strcpy (p,s);

return p;} else

- 42 -

Page 43: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

return NULL;}

Observaţii:1o. În fişierul stdio.h există definiţia constantei NULL.2o. Fişierul alloc.h s-a inclus deoarece conţine prototipul funcţiei

malloc.3o. Fişierul string.h conţine prototipurile funcţiilor strlen şi strcpy.4o. Funcţia malloc se apelează pentru a rezerva strlen(s)+1 octeţi; strlen

returnează numărul de octeţi cuplaţi de caracterele proprii ale lui s (fără caracterul NULL). Cum în zona de memorie rezervată prin malloc se păstrează şi caracterul NULL, lungimea returnată de funcţia strlen s-a mărit cu 1.

5o. Pointerul returnat de malloc a fost convertit spre char *, deoarece el este de tip void *. Acest pointer se atribuie lui p, deci p pointează spre începutul zonei de memorie alocate prin apelul funcţiei malloc. Se testează dacă acest pointer este diferit de NULL (deci dacă s-a putut aloca memoria de dimensiunea cerută). În caz afirmativ, se transferă şirul prin apelul funcţiei strcpy, returnându-se apoi valoarea pointerului p.

6.5. POINTERI SPRE FUNCŢIINumele unei funcţii este un pointer spre funcţia respectivă. El poate fi

folosit ca parametru efectiv la apeluri de funcţii. În felul acesta, o funcţie poate transfera funcţiei apelate un pointer spre o funcţie. Aceasta, la rândul ei, poate apela funcţia care i-a fost transferată în acest fel.

Exemplu:Un exemplu matematic în care este nevoie de un astfel de transfer este cel

cu privire la calculul aproximativ al integralelor definite. Să presupunem că dorim să calculăm integrala definită din funcţia f(x), între limitele a şi b, folosind formula trapezului:

I= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)undeh=(b-a)/h.

În continuare construim o funcţie care calculează partea dreaptă a acestei relaţii. Numim aria_trapez această funcţie.

Observaţii:1o. Deoarece funcţia f(x) din relaţia de mai sus nu este definită în acest

moment, ea trebuie să figureze printre parametrii funcţiei aria_trapez, împreună cu limitele de integrare şi valoarea lui n.

2o. Funcţia aria_trapez returnează valoarea aproximativă a integralei şi ea se va apela printr-o expresie de atribuire, de exemplu:

aria=aria_trapez (a, b, n, f);3o. Funcţia aria_trapez returnează o valoare flotantă în dublă precizie.

De asemenea, şi funcţia f(x) returnează o valoare flotantă în dublă precizie. De aici rezultă că prototipul funcţiei aria_trapez este următorul:

double aria_trapez (double a, double b, int n, double (*f)());saudouble aria_trapez (double, double, int , double (*)());4o. Este necesar ca înaintea apelului funcţiei aria_trapez funcţia f(x) să

fie definită sau să fie prezent prototipul ei , de exemplu:double f();5o. Construcţia double (*f) () se interpretează în felul următor:- *f înseamnă că f este un pointer;- (*f)() înseamnă că f este un pointer spre o funcţie;- double (*f) () înseamnă că f este un pointer spre o funcţie care

returnează o valoare flotantă în dublă precizie.6o. Trebuie să se includă *f între paranteze, deoarece construcţia double

*f(); este corectă, dar înseamnă altceva, parantezele rotunde fiind prioritare operatorului unar *. În acest caz, se declară f ca o funcţie ce returnează un pointer spre o valoare flotantă în dublă precizie.

7o. Ultimul parametru formal al funcţiei aria_trapez corespunde parametrului efectiv f şi deci el trebuie declarat ca şi pointer spre o funcţie ce returnează o valoare flotantă în dublă precizie. Conform observaţiei 5), dacă p este numele parametrului formal ce corespunde parametrului efectiv f, atunci p se declară astfel:

double (*p)();- 43 -

Page 44: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

8o. În corpul funcţiei aria_trapez va trebui să apelăm funcţia f(x) pentru a calcula valorile:

f(a), f(b), f(a+h), . . . , f(a+(n-1)h).În momentul programării funcţiei aria_trapez, nu se cunoaşte numele

funcţiei concrete, ci numai pointerul p spre ea. De aceea, vom înlocui numele funcţiei prin *p, deci vom folosi apelurile:

(*p)(a), (*p)(b), (*p)(a+h), . . . ,(*p)(a+(n-1)h)

double aria_trapez(double x, double y, int m, double(*p)());{ double h,s;

int i;h=(y-x)/m;for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);s+=((*p)(x) + (*p)(y))/2;s=h*s;return s;

}Vom utiliza funcţia aria_trapez pentru a calcula integrala definită din

funcţia sin(x2) pe intervalul [0,1], cu o eroare mai mică decât 10-8. Vom nota cu In următoare sumă:

In= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)Paşii algoritmului sunt următorii:Pasul 1. Se alege o valoare iniţială pentru n, de exemplu 10.Pasul 2. Se calculează In.Pasul 3. Se calculează I2n prin dublarea lui n.Pasul 4. Dacă |In-I2n| < 10-8, algoritmul se întrerupe şi valoarea

integralei, cu precizia admisă, este I2n; altfel se dublează n, se pune In=I2n; n, şi se trece la pasul 3.

#define A 0.0#define B 1.0#define N 10#define EPS 1e-8#include <stdio.h>#include <math.h>double sinxp(double); // prototipul functiei sin(x*x)double aria_trapez(double, double, int, double (*)());void main (void) // functia principala{ int n=N; double in, i2n, vabs; in=aria_trapez (A, B, n, sinxp); do {n=n*2;i2n=aria_trapez(A, B, n, sinxp);if ((vabs= in-i2n) < 0) vabs = -vabs;in=i2n; } while (vabs >= EPS); printf (“valoarea integralei este : %g.10\n”,i2n);}double aria_trapez(double x, double y, int m, double(*p)());{ double h,s;

int i;h=(y-x)/m;for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);s+=((*p)(x) + (*p)(y))/2;s=h*s;return s;

}

double sinxp (double x){ return sin (x*x); }

- 44 -

Page 45: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

6.6. TRATAREA PARAMETRILOR DIN LINIA DE COMANDĂÎn linia de comandă folosită la apelul execuţiei unui program se pot

utiliza diferiţi parametri. Aceşti parametri pot fi utilizaţi folosind parametrii argc şi argv ai funcţiei principale.

Parametrul argc este de tip întreg şi indică numărul de parametri din linia de comandă.

Parametrul argv este un tablou de pointeri spre zonele în care sunt păstraţi parametrii liniei de comandă. Aceştia se consideră şiruri de caractere.

Astfel antetul funcţiei principale va fi :main (int argc, char *argv[ ])Exemplu:Considerăm că la lansarea programului prog s-au furnizat parametrii:MARTIE 1956În acest caz argc=4, iar tabloul argv conţine pointerii: - argv[0] - pointer spre numele programului (calea, numele şi extensia

.EXE - argv[1] - pointer spre şirul “31”; - argv[2] - pointer spre şirul “MARTIE”; - argv[3] - pointer spre şirul “1991”.Observaţii:1o. Lansarea unui program se face cu prima instrucţiune a funcţiei

principale. Deci parametrii argc şi argv au deja în acest moment valorile indicate mai sus, putând fi analizaţi chiar cu prima instrucţiune executabilă.

2o. În mod frecvent, aceşti parametrii reprezintă diferite opţiuni ale programului, date calendaristice, nume de fişiere, etc.

3o. argv[0] este întotdeauna pointerul spre numele fişierului cu imaginea executabilă a programului.

void main ( int argc, char *argv[]) // va afisa parametrii din linia de comanda

{ int i; for (i=0; i<argc; i++;) printf (“%s\n”,argv[i]);}

6.7. MODIFICATORUL const Am văzut anterior că o constantă se defineşte prin caracterele care intră

în compunerea ei. De asemenea, în acelaşi capitol s-a arătat că putem atribui un nume unei constante printr-o construcţie #define. Un astfel de nume se spune că este o constantă simbolică şi el se substituie prin şirul de caractere care şi corespunde, în faza de preprocesare.

Un alt mod de a defini o constantă este acela de a folosi modificatorul const într-o declaraţie. Printr-o astfel de declaraţie, unui nume i se poate atribui o valoare constantă. În acest caz, numele respectiv nu mai este tratat de preprocesor şi el poate fi folosit în program în mod analog cu numele variabilelor. Unui astfel de nume declarat cu ajutorul modificatorului const nu i se poate schimba valoarea printr-o expresie de atribuire, ca şi unei variabile obişnuite.

Formatele declaraţiei cu modificatorul const sunt următoarele:tip const nume = valoare;const tip nume = valoare;tip const nume;const tip nume;const nume = valoare;const nume;

Exemplu:void main (void)

{ const i=10; // i devine egal cu 10; nu este posibila o atribuire i=0const pi = 3.1415926char *const s=”martie”;// s este un pointer constant spre zona in care este

// pastrat sirul de caractere “martie”. Valoarea lui s // nu poate fi schimbata dar continutul zonei spre

// care pointeaza s poate fi schimbat *s= ‘1’; // schimba litera m cu 1 *(s+1)=’2’ // schimba litera a cu 2

char const *s=”aprilie”; // s este un pointer spre o zona constanta.- 45 -

Page 46: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

// valoare lui s poate schimbata dar sirul “aprilie” // nu poate fi modificat

const char *s=”aprilie”// este identica cu declaratia de mai sus.}Modificatorul const se foloseşte frecvent la declararea parametrilor

formali de tip pointer. O astfel de declaraţie are formatul:const tip *nume_parametru_formal;Un parametru formal declarat prin construcţia :

tip *nume_parametru_formal;corespunde unui parametru efectiv a cărui valoare este o adresă. La apel,

valoarea parametrului formal devine egală cu această adresă. Datorită acestui fapt, funcţia apelată poate să modifice data aflată la adresa respectivă. Dacă se foloseşte modificatorul const utilizat la declararea unui astfel de parametru formal atunci se interzice funcţiei apelate să modifice data de la adresa recepţionată la apel de către parametrul formal corespunzător. Acest mecanism este folosit frecvent în cazul funcţiilor de tratare a şirurilor de caractere.

De exemplu funcţia strlen din biblioteca standard a limbajului C are prototipul:

unsigned strlen (const char *s);Ea se apelează prin expresii de atribuire de forma:i=strlen(x);unde x este un pointer spre o zonă de memorie în care se află un şir de

caractere.Funcţia strlen determină lungimea şirului aflat la adresa recepţionată de

către parametrul s. Ea nu are voie să modifice şirul respectiv şi din această cauză parametrul s se declară folosind modificatorul const.

6.8. STIVAPrin stivă (stack în engleză) înţelegem o mulţime ordonată de elemente la

care accesul se realizează conform principiului ultimul venit primul servit. În engleză stiva se mai numeşte şi listă LIFO (Last In First Out).

O modalitate simplă de a implementa o stivă este păstrarea elementelor ei într-un tablou unidimensional. În acest tablou se vor păstra elementele stivei unul după altul. De asemenea, ele se pot scoate din tablou în ordinea inversă păstrării lor. La un moment dat se poate scoate ultimul element pus pe stivă şi numai acesta.

Despre ultinul element pus în stivă se spune că este vârful stivei, iar despre primul element că este baza stivei.

Accesul este pemis doar la vârful stivei:un element se poate pune pe stivă numai după elementul aflat în vârful

stivei şi după această operaţie el ajunge vârful stivei;se poate scoate de pe stivă numai elementul aflat în vârful stivei şi după

această operaţie în vârful stivei rămâne elementul care a fost pus pe stivă înaintea lui.

Vom numi stack tablou de tip int afectat stivei şi next variabila care indică prima poziţie liberă din stivă. Deci stack[0] este baza stivei iar stack[n] va fi vârful stivei. Vom defini mai multe funcţii asociate tabloului stack:

- push funcţia care pune un element în stivă;- pop funcţia care scoate un element din stivă;- clear funcţia de iniţializare a stivei; după apelul ei stiva devine

vidă;#define MAX 1000static int stack[1000];static next = 0; // indicele pentru baza stivei

void push(int x) // pune pe stiva valoarea lui x{ if (next < MAX)

stack [next++]=x;else

printf (“stiva este depasita\n”);}int pop() // scoate elementul din varful stivei si returneaza valoarea lui{ if (next >0)

- 46 -

Page 47: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

return stack [--next];else { printf (“stiva este vida\n”);

return 0; }}void clear(void) // videaza stiva{ next=0;}

LECŢIA 7.RECURSIVITATE

Spunem că o funcţie C este recursivă dacă ea se autoapelează înainte de a se reveni din ea. Funcţia se poate reapela fie direct, fie indirect (prin intermediul altor funcţii).

La fiecare apel al unei funcţii, parametrii şi variabilele locale se alocă pe stivă într-o zonă independentă. De asemenea, orice apel recursiv al unei funcţii va conduce la o revenire din funcţie la instrucţiunea următoare apelului respectiv. La revenirea dintr-o funcţie se realizează curăţarea stivei, adică zona de pe stivă afectată la apel parametrilor şi variabilelor automatice se eliberează.

Un exemplu simplu de funcţie apelata recursiv este funcţia de calcul al factorialului. Putem defini recursiv funcţia factorial astfel:

factorial(n)= 1, dacă n=0factorial(n)=n*factorial(n-1), dacă n>0

În limbajul C avem :

double factorial (int){ if (n= = 0) return 1.0; else return n*factorial(n-1);}Observaţii:1o. În general, o funcţie recursivă se poate realiza şi nerecursiv, adică

fără să se autoapeleze.2o. De obicei, recursivitatea nu conduce nici la economie de memorie şi

nici la execuţia mai rapidă a programelor. Ea permite însă o descriere mai compactă şi mai clară a funcţiilor. Acest lucru rezultă şi din exemplul de mai sus de calcul al factorialului.

3o. În general, funcţiile recursive sunt de preferat pentru procese care se definesc recursiv. Există şi excepţii. De exemplu algoritmul de generare a permutărilor de n obiecte poate fi descris recursiv astfel: având în memorie toate cele (n-1)! permutări, atunci permutările de n obiecte se generează înserând pe n în toate poziţiile posibile ale fiecărei permutări de n-1 obiecte. Dar ne aducem aminte că 10!=3628800 şi capacitatea stivei se depăşeşte repede.

Exemple:Programul determină recursiv cmmdc (algoritmul lui Euclid) a două numere

întregi (de tip long):cmmdc (a,b) = b, dacă a%b =0 (restul împărţirii lui a la b e zero)cmmdc (a,b) = cmmdc (b,a%b), în caz contrar.

#include <iostream.h> #include <conio.h>

long cmmdc(long a, long b){ if (!(a % b)) return b; else return cmmdc(b, a % b);}void main(void){ long x,y; clrscr(); cout << "dati un numar natural=";

- 47 -

Page 48: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

cin >> x; cout << "dati alt numar natural="; cin >> y; cout << "cmmdc(" << x << "," << y << ")=" << cmmdc (x,y);}Am folosit funcţiile de intrare / ieşire cin şi cout, imediat se observă

modul lor de utilizare.Programul determină recursiv suma unor elemente de tablou unidimensional

a[1]+a[2]+ . . . + a[n]

#include <iostream.h>#define MAX 100int a[MAX];// suma(n)= 0, daca n=0// suma(n)=suma(n-1) + a[n] daca n>0int suma(int n){ if (!n) return 0; else return a[n]+suma(n-1);}

void main(void){int n,i; cout << "dati n= "; cin >> n; for (i=1; i<=n; i++) { cout << "a[" << i << "]= ";cin >> a[i]; } cout << "suma numerelor este " << suma(n);}

Programul determină recursiv termenul al n-lea din şirul lui Fibonacci definit după cum urmează:

fibonacci[0]=0fibonacci[1]=1fibonacci[n]=fibonacci[n-1]+fibonacci[n-2], dacă n>1

#include<iostream.h>

long fibonacci (long n){if (!n) return 0; else if (n==1) return 1; else return fibonacci(n-1) + fibonacci(n-2);}

void main (void){ long n; cout << "dati n = "; cin >> n; cout << "fibo(" << n << ") =" << fibonacci (n);}Programul determina maximul dintr-un vector de numere astfel:

M(n)= a[1] dacă n=1M(n)= max { M(n-1),a[n] } dacă n>1

#include<iostream.h>#define MAX(x,y) (x > y ? x : y)int a[100];

int M(int n){ if (n= =1) return a[1]; else return MAX (M(n-1), a[n]);}

- 48 -

Page 49: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

void main(void){int n,i; cout << "dati n="; cin >> n; for (i=1; i<=n; i++) { cout << "a[" << i << "]= "; cin >> a[i]; } cout << "maximul este " << M(n);}5) Programul afisează un şir de caractere în mod recursiv, caracter cu

caracter, considerând că şirul de caractere este format din primul caracter(capul) + restul şirului de caractere (coada).

#include <iostream.h>#include <conio.h>#define max 100char sir [max];int n;

void afis (int m){ if (m = = n+1) return; else { cout << sir[m];

afis(m+1); }

}

void main (void){int i; do { cout << "\ndati lungimea sirului =";cin >> n; } while ( (n< 0) || (n > max)); for(i=1; i<=n; i++) { cout << "sir[" << i << "]=";

cin >> sir[i]; } afis(1); getch();}6) Programul ce urmează e oarecum asemănător cu exemplul anterior doar că

afişează şirul de caractere de la sfârşit spre început.#include <iostream.h>#include <conio.h>#define max 100char sir [max];void afis (int m){ if (m==0) return; else { cout << sir[m];

afis(m-1);}

}void main (void){int n,i; do {cout << "\ndati lungimea sirului ="), cin >> n;} while ( (n< 0) || (n > max)); for(i=1; i<=n; i++) { cout << "sir[" << i << "]=";cin >> sir[i]; } afis(n); getch();}

7) Programul sortează prin metoda quicksort un vector de numere întregi:- 49 -

Page 50: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

#define dim 50#include <stdio.h>#include <conio.h>int x[dim+1],i,n;

void tipsir (){ for (i=1; i<=n; i++) { printf("%3d",x[i]);if (!(i % 20)) printf ("\n"); }}

void quik(int st, int dr){int i,j,y; i=st; j=dr; y=x[i]; do { while ((x[j] >= y) && (i<j)) j - -; x[i]=x[j]; while ((x[i] <= y) && (i<j)) i++; x[j]=x[i]; } while (i != j); x[i]=y; if (st < i-1) quik(st,i-1); if (i+1 < dr) quik(i+1,dr); x[j]=x[i];}

void citire (void){ int cod = 0; n = dim+1; while ( n <= 0 || n > dim || ! cod ) { printf ("\ndati dim. sir:"); cod=scanf ("%d",&n); } i = 1; while (i<=n) { printf ("x[%2d]=",i); scanf ("%d", &x[i]); i++; }}

void main(void){ clrscr(); citire(); clrscr(); printf ("\n\nsir initial\n"); tipsir(); quik(1,n); printf ("\n\nsir sortat\n"); tipsir(); getche();}

- 50 -

Page 51: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 8.STRUCTURI, TIPURI UTILIZATOR

După cum am văzut datele de acelaşi tip se pot grupa în tablouri. Limbajul C permite gruparea unor date de tipuri diferite sub alte forme de organizare numite structuri.

Tablourile au un tip şi anume tipul comun elementelor lor. Astfel, distingem tablouri de tip întreg, de tip caracter, de tip flotant, etc. În cazul structurilor, nu mai avem un tip comun. Fiecare structură reprezintă un nou tip de date, tip care se introduce prin declaraţia structurii respective.

Un exemplu simplu de structură este data calendaristică, cu componentele următoare:

ziua;luna;anul.unde: ziua şi anul sunt date de tip întreg iar luna este un tablou de

caractere.Structura ca şi tabloul, este o mulţine ordonată de elemente. În

exemplul de mai sus se consideră că ziua este primul ei element, luna este al doilea iar anul este ultimul ei element. Trebuie să precizăm că referirea la componentele unei structuri nu se mai face cu ajutorul indicilor ci prin calificare.

8.1. DECLARAŢIA DE STRUCTURĂO structură se poate declara în mai multe feluri, astfel:

Formatul 1:struct nume_structura

{ lista_declaratii};

Cu ajutorul acestui format se introduce un nou tip de dată cu numele nume_structură. Lista de declaraţii este formată din declaraţii obişnuite. Tipul data_calendaristica îl putem introduce astfel:

struct data_calendaristica{ int ziua; char luna[11]; int anul;};

O astfel de declaraţie se numeşte declaraţie de tip. Să reţinem că unui nou tip de date nu i se alocă memorie, el este doar contabilizat ca un nou tip utilizator pe lângă tipurile predefinite ale limbajului C.

Formatul 2:struct nume_structura

{ lista_declaratii}lista_variabile;

Un astfel de format introduce tipul utilizator nume_structura şi declară o listă de varibile în care fiecare element din listă are tipul nume_structură. Prin exemplu următor se introduc variabilele dc1 şi dc2 ca date elementare de tipul data_calendaristica şi tabloul dc de 13 componente.

- 51 -

Page 52: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

struct data_calendaristica{ int ziua; char luna[11]; int anul;} dc1, dc2, dc[13];

Formatul 3:struct { lista_declaraţii

} lista_variabile;

Acest format se foloseste dacă nu vrem sa dăm nume noului tip structurat şi totodată dacă nu mai vrem să-l folosim. Deci nu vom mai pute declara alte date de tipul structurat nou introdus pentru că tipul nu are nume.

Exemplu:struct { int ziua;

char luna[11]; int anul;} dc1, dc2, dc[13];

S-au declarat varibilele dc1, dc2 şi tabloul dc având noul tip structurat utilizator dar nu se mai doreşte să declarăm alte date de acest tip.

Observaţii:1o. Dacă se foloseşte formatul 1 atunci pentru a declara date de tipul

utilizator nou introdus se foloseşte o construcţie de forma:struct nume_ structura lista_variabile; Compilatorul alocă memorie varibilelor din lista de variabile, tratând

această construcţie ca şi declaraţiile obişnuite.

2o. Componentele unei structuri pot fi ele însele date structurate. O componentă care nu este structurată se numeşte componentă elementară.

3o. Ca şi în cazul celorlalte tipuri de variabile se pot defini structuri globale, statice sau automatice. Structurile statice se declară precedând declaraţiile lor prin cuvântul static, iar cele externe prin cuvântul cheie extern.

4o. Elementele unei date de tip structură pot fi iniţializate după modelul iniţializării variabilelor care au tipuri predefinite.

Exemple:1) Introducem tipul utilizator data_calendaristica astfel:

struct data_calendaristica{ int ziua; char luna[11]; int anul;};

pentru a iniţializa o dată de tipul data_calendaristică vom scrie:

struct data_calendaristica dc1={31, “martie”, 1956};

2) Dacă declarăm un nou tip date_personale şi în care vrem să folosim tipul data_calendaristica, vom scrie:

struct date_personale{ char nume[30]; char adresa[50]; struct data_calendaristica data_nasterii,

data_angajarii; };

8.2. ACCESUL LA ELEMENTELE UNEI STRUCTURIPentru a avea acces la componentele unei date structurate va trebui să

folosim o calificare de forma:nume_data_structurata.nume_componenta

- 52 -

Page 53: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Astfel dacă avem tipul structurat data_calendaristica introdus in exemplele anterioare şi declarăm data dc astfel:

struct data_calendaristica dc;

atunci pentru a ne referi la componentele datei dc vom folosi construcţiile:

dc.ziuadc.anuldc.luna (atenţie este pointer spre caractere)

Dacă avem declarat un tablou astfel:struct data_calendaristica tdc[10];

atunci pentru fiecare componentă i ne vom referi astfel:

tdc[i].ziua tdc[i].anultdc[i].luna (este pointer)tdc[i].luna[0], tdc[i].luna[1], . . . , tdc[i].luna[11]

Ca şi tablourile structurile se pot transfera prin parametrii, transferând un pointer spre data structurată respectivă, adică adresa de început a zonei alocate structurii. Deci, printr-un apel de forma:

functie(&data_structurata);

se transferă funcţiei functie adresa de început a zonei alocate structurii data_structurata. Dacă data_structurata este o structura de tipul tip, atunci antetul funcţiei functie este următorul:

void functie(tip *p)

unde p este pointer spre tipul structurat tip.

Pentru data structurată dc de tipul data_calendaristica antetul funcţiei functie este:

void functie(struct data_calendaristica *p)

iar apelul pentru data dc se face functie(&dc);

Printr-un astfel de apel, funcţia apelată nu are acces la numele datei structurate transferate, ci numai la pointerul spre ea. De aceea se pune problema accesului la componentele datei structurate prin pointerul la ea. În acest caz numele datei structurate se va înlocui prin *p. Deci, în cazul datei structurate dc, transferate ca şi mai sus, în locul construcţiei

dc.zivom scrie:(*p).zi

înlocuind numele datei structurate dc prin *p, unde p este un pointer spre dc.

Observaţie:1o. Parantezele rotunde din construcţia de mai sus sunt obligatorii,

deoarece punctul este un operator prioritar operatorului unar *.2o. Construcţia de mai sus poate fi înlocuită prin p->zi care este

identică cu ea. Simbolul -> se compune din caracterele ‘-‘ şi ‘>’ scrise unul după celălalt fără spaţiu între ele. El se numeşte săgeată şi este considerat a fi un operator cu aceeaşi prioritate ca şi punctul, deci de prioritate maximă.

8.3. ATRIBUIRI DE NUME PENTRU TIPURI DE DATEDupă cum ştim tipurile de bază ale limbajului C, numite şi tipuri

predefinite se identifică printr-un cuvânt cheie (int, char, float, etc). Totodată prin instrucţiunea struct, programatorul poate să introducă un tip nou.

- 53 -

Page 54: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Programatorul poate să atribuie un nume unui tip (predefinit sau utilizator) cu ajutorul construcţiei:

typedef tip nume_nou_tip;unde:tip este numele unui tip predefinit sau al unui tip utilizator (introdus

cu struct);nume_nou_tip este noul nume atribuit tipului respectiv.După ce s-a atribuit un nou nume unui tip, numele respectiv poate fi

utilizat pentru a declara date de acel tip, la fel cum se utilizează în declaraţii cuvintele cheie int, char, float, etc.

Observaţii:1o. De obicei numele atribuit unui tip se scrie cu litere mari.2o. Un exemplu de astfel de nume există în fişierul stdio.h pentru tipul

fişier, căruia i s-a atribuit numele FILE.Exemple:Fie declaraţiile:typedef int INTREG;typedef float REAL;În continuare, denumirile INTREG şi REAL se pot folosi la fel ca şi

cuvintele cheie int şi float. Cu alte cuvinte, declaraţia:

INTREG i, j, tablou[10];

este identică cu declaraţia următoare:

int i, j, tablou[10];

Analog:REAL x, y, z;

este identică cu declaraţia:

float x, y, z;

typedef struct data_calendaristica{ int ziua; char luna[11];

int anul;} DC;

Prin această declaraţie se atribuie denumirea DC tipului structurat data_calendaristica. În continuare putem declara date de tip DC:

DC data_nasterii, data_angajarii;DC data_curenta ={31,”august”,1998};

typedef int *PI;Prin această declaraţie se introduce un sinonim pentru tipul pointer spre

întregi: int *.Putem să declarăm în continuare pointeri spre întregi astfel:

PI p;care este echivalentă cu:

int *p;

4) Declaraţia typdef struct { double real;

double imaginar;} COMPLEX;

introduce numele COMPLEX pentru datele de tip complex.

Funcţia următoare returnează modulul unui număr complex:- 54 -

Page 55: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

typedef struct{ double real;

double imaginar;} COMPLEX;

#include <math.h>double modul (COMPLEX *x) // returneaza modulul numarului

// spre care pointeaza x{ return sqrt (x->real * x->real + x->imaginar * x->imaginar);

}

8.4. UNIUNELimbajul C oferă utilizatorului posibilitatea de a folosi aceeaşi

zonă de memorie pentru a păstra date de tipuri diferite în momente diferite ale execuţiei programului. Astfel, de exemplu, putem utiliza o zonă de memorie pentru a păstra la un moment dat o dată flotantă, iar ulterior să reutilizăm aceeaşi zonă pentru o dată întreagă sau de tip pointer. Reutilizările zonelor de memorie conduc la utilizarea mai eficientă a acesteia, uneori putându-se obţine o economie substanţială a spaţiului de memorie alocat programului.

O uniune se declară printr-o construcţie asemănătoare declaraţiei de structură. Deosebirea constă în înlocuirea cuvântului struct prin union:

union nume { tip_membru_1; . . . tip_membru_2;}

Exemplu:union u

{ int i; float f; double d;};

Prin această declaraţie s-a definit tipul de date u. În continuare, putem declara date de tipul u printr-o declaraţie de forma:

union u u1;

unde u1 este o dată de tip u căreia i se alocă o zonă de memorie care poate fi utilizată pentru a păstra date de tipurile int, float sau double. Deoarece tipul double necesită memoria cea mai mare se alocă 8 octeţi. Astfel zona u1 poate păstra pe oricare din celelalte componente ale uniunii dar în momente diferite ale execuţiei programului.

Accesul la componentele unei uniuni se face la fel ca şi în cazul structurilor. Astfel, pentru a ne referi la componenta i a uniunii u1 definită în exemplul anterior folosim construcţia:

u1.i

sau dacă p este pointer spre tipul u declarat prin union u *p;atunci construcţiap -> i

permite accesul la componenta i a uniunii spre care pointează p.

Pentru a evita erorile legate de evidenţa în fiecare moment a datei care se prelucrează se ataşează unei uniuni o dată menită să indice componenta curentă. Această dată este specificată pentru fiecare uniune. De exemplu pentru

- 55 -

Page 56: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

uniunea u1 definită anterior este important să se ştie dacă zona de memorie conţine un întreg, un flotant în simplă precizie sau un flotant în dublă precizie. Se definesc trei constante simbolice:

#define INTREG 1#define F_SIMPLU 2#define F_DUBLU 3

Modificăm tipul u ataşând data tip_curent de tip int astfel:struct u

{int tip_curent; union { int i;

float f;double d;

} uu;};

Declarăm structura us astfel:struct u us;

În acest caz, în momentul în care se păstrează o dată în zona rezervată uniunii, se atribuie componentei tip_curent una din constantele definite anterior:

INTREG, dacă se păstrează un întreg;F_SIMPLU dacă se păstrează un flotant în simplă precizie;F_DUBLU dacă se păstrează un flotant în dublă precizie.Astfel când se foloseşte componenta de tip int se va asocia atribuirea:

us.tip_curent=INTREG;

Analog se vor folosi ţi atribuirile următoare când se vor folosi componentele de tip float sau de tip double:

us.tip_curent=F_SIMPLU;respectiv:us.tip_curent=F_DUBLU;

În felul acesta, se poate testa, în fiecare moment, tipul de dată prezent în zona rezervată. Aceasta se poate face printr-o secvenţă de instrucţiuni if sau prin intermediul instrucţiunii switch.

if (us.tip_curent = = INTREG) // se foloseste us.uu.i

else if (us.tip_curent = = FSIMPLU) // se foloseste us.uu.felse if (us.tip_curent = = FDUBLU) // se foloseste us.uu.d

else eroare

sau folosind switch avem o construcţie mai clară de forma:

switch (us.tip_curent){ case INTREG:

// se foloseste us.uu.ibreak;

case FSIMPLU:// se foloseste us.uu.fbreak;

case FDUBLU// se foloseste us.uu.d

break; default:

// eroare}Programul următor calculează ariile pentru următoarele figuri geometrice:cerc;dreptunghi;pătrat;triunghi.

- 56 -

Page 57: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Programul citeşte datele pentru o figură geometrică, calculează aria figurii respective şi scrie rezultatul:

La intrare se folosesc următoarele formate:- pentru cerc C raza;- pentru dreptunghi D lungime laţime;- pentru pătrat P latură;- pentru triunghi T latură latură latură;- sfârşit fişier EOF.În cazul triunghiului, se utilizează formula lui HERON pentru calculul

ariei:aria = sqrt (p*(p-a)(p-b)(p-b))unde p este semiperimetrul, iar a, b, c sunt cele 3 laturi.#include <stdio.h>#include <math.h>#define PI 3.14159265#define EROARE -1#define CERC 1#define PATRAT 2#define DREPT 3#define TRIUNGHI 4typedef struct { int tip; // tipul figurii

union { double raza // cerc double lp ; // patrat double ld[2] ; // dreptunghi double lt[3] ; // triunghi } fig;

}FIG;

void main (void) // calculeaza arii {double aria,p;int i;char car[2];FIG zfig;for(; ;) // citeste primul caracter,el defineste

tipul figurii geometrice{ printf(“se cere o litera mare\n”); if ((i = scanf("%1s",car)) == EOF) break; if (i != 1) { printf (" se cere o litera mare\n"); continue; } zfig.tip = EROARE; switch(car[0]){case 'C': // cercprintf("se cere raza cercului in flotanta\n");

i = scanf("%lf", &zfig.fig.raza);if(i != 1)

{ printf("se cere raza cercului in flotanta\n"); break;

}zfig.tip = CERC; // se pastreaza tipul figurii

break;

case 'P': // patratprintf("se cere latura patratului in flotanta\n");

i = scanf("%lf",&zfig.fig.lp);if( i !=1)

{ printf("se cere latura patratului in flotanta\n"); break;

}zfig.tip = PATRAT;break;

- 57 -

Page 58: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

case 'D': // dreptunghiprintf("se cer laturile dreptunghiului in flotanta\n");

i = scanf("%lf %lf",&zfig.fig.ld[0],&zfig.fig.ld[1]);if(i != 2)

{ printf("se cer laturile dreptunghiului in flotanta\n"); break;

}zfig.tip = DREPT;break;

case 'T': // triunghiprintf("se cer laturile triunghiului in flotanta\n");

i = scanf("%lf %lf %lf", &zfig.fig.lt[0], &zfig.fig.lt[1],&zfig.fig.lt[2]);

if(i != 3){ printf("se cer laturile triunghiului in flotanta\n");

break; }

zfig.tip =TRI;break;printf("laturile nu formeaza un triunghi\n");break;

default: printf("se cere una din literele urmatoare\n"); printf("C pentru cerc\n"); printf("P pentru patrat\n"); printf("D pentru dreptunghi\n"); printf("T pentru triunghi\n");

} // sfarsit switch

switch (zfig.tip){case CERC: // aria cercului

printf("raza=%g aria=%g\n", zfig.fig.raza, PI*zfig.fig.raza*zfig.fig.raza);

break; case PATRAT: // aria patratului

printf("latura =%g aria=%g\n",zfig.fig.lp, zfig.fig.lp*zfig.fig.lp);

break; case DREPT: // aria dreptunghiului

printf("lungimea =%g latimea =%g\n", zfig.fig.ld[0], zfig.fig.ld[1]); printf("aria=%g\n", zfig.fig.ld[0]*zfig.fig.ld[1]);

break; case TRIUNGHI: // aria triunghiului

p=(zfig.fig.lt[0] + zfig.fig.lt[1] + zfig.fig.lt[2])/2;if(p>zfig.fig.lt[0] && p>zfig.fig.lt[1] && p>zfig.fig.lt[2])

{p=p*(p-zfig.fig.lt[0])*(p-zfig.fig.lt[1])* (p-zfig.fig.lt[2]); printf("a=%g b=%g c=%g\n", zfig.fig.lt[0], zfig.fig.lt[1],

zfig.fig.lt[2]); printf("aria = %g\n",sqrt(p)); }else { printf (“ laturile nu formeaza un triunghi”); break; }

default : // avans pana la newline sau EOFwhile ((i = getchar()) != ‘\n’ && i != EOF);

} // sfarsit switch if (i = = EOF) break; } // sfarsit for} // sfarsit main

8.5. CAMP

- 58 -

Page 59: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Limbajul C permite utilizatorului definirea şi prelucrarea datelor pe biţi. Utilizarea datelor pe biţi este legată de folosirea indicatorilor care de obicei sunt date care iau numai două valori 0 sau 1.

Nu este justificat ca un astfel de indicator să fie păstrat ca un întreg pe 16 biţi şi nici măcar pe un octet. Indicatorul poate fi păstrat pe un singur bit. În acest scop, limbajul C oferă posibilitatea de a declara date care să se aloce pe biţi (unul sau mai mulţi biţi). Acest lucru îşi găseşte aplicare în programele de sistem. Astfel, de exemplu, atributele variabilelor dintr-o tabelă de simboluri pot fi păstrate pe biţi, ceea ce conduce la o economisire substanţială a memoriei ocupate de tabela respectivă.

Prin camp înţelegem un şir de biţi adiacenţi conţinuţi într-un cuvânt calculator. Câmpurile se grupează formând o structură.

Un câmp se declară ca şi o componentă a unei structuri şi el are tipul unsigned (întreg fără semn). Totodată în declaraţia câmpului se indică şi dimensiunea lui în biţi.

În general, o structură cu componente câmpuri are forma:struct

{ camp1; . . . campn;} nume;

unde campi (i=1,...,n) are unul din formatele de mai jos:unsigned nume : lungime_în_biţi

sau : lungime_în_biţi

Exemplu:struct

{ unsigned a:1; unsigned b:1; unsigned c:2; unsigned d:2; unsigned e:3;} indicatori;

Data indicatori se alocă într-un cuvânt calculator, adică pe 16 biţi. Componentele ei sunt:

a un bit;b un bit;c doi biţi;d doi biţi;e trei biţi.La câmpuri ne putem referi la fel ca şi la componentele oricărei

structuri. Deci la indicatorii de mai sus ne putem referi prin următoarele construcţii:

indicatori.aindicatori.bindicatori.cindicatori.dindicatori.e

Alocarea biţilor este dependentă de calculator. De obicei biţii se alocă de la dreapta spre stânga ca în figura de mai jos:

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 a b c d e

- 59 -

Page 60: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Observaţii:1o. Dacă un câmp nu poate fi alocat în limitele unui cuvânt, el se alocă

în întregime în cuvântul următor.2o. Nici un câmp nu poate avea o dimensiune mai mare decât 16 biţi.3o. Formatul fără nume (al doilea format) pentru câmp se foloseşte pentru

cadraje. Acest lucru este util atunci când sunt zone de biţi neutilizate în cadrul unui cuvânt. De asemenea, utilizarea formatului cu lungime egală cu zero permite ca alocarea câmpurilor următoare lui să se facă în cuvântul următor.

4o. O structură care are şi componente câmpuri poate avea şi componente obişnuite.

5o. Nu se pot defini tablouri de câmpuri.6o. Unui câmp nu i se poate aplica operatorul adresă.

Câmpurile se utilizează frecvent la scrierea unor programe de sistem, cum ar fi : drivere pentru periferice, compilatoare, etc.

Utilizarea câmpurilor poate conduce la programe cu o portabilitate redusă. Totodată, accesul la date pe biţi conduce la creşterea numărului de operaţii, fiind necesare deplasări şi operaţii pe biţi suplimentare, fapt ce poate conduce atât la creşterea timpului de execuţie a programelor, cât şi la creşterea memoriei utilizate. Ori datele pe biţi se folosesc chiar în ideea de a economisi memorie.

8.6. TIPUL ENUMERATTipul enumerat permite utilizatorului să folosească în program nume

sugestive în locul unor valori numerice. De exemplu, în locul numărului unei luni calendaristice, se poate folosi denumirea ei:

ianfebmarÎn locul valorilor 0 şi 1 se pot folosi cuvintele FALS şi ADEVRAT.Prin aceasta, se introduce o mai mare claritate în programe, deoarece

valorile numerice sunt înlocuite prin diferite sensuri atribuite lor.Un tip enumerat se introduce printr-o declaraţie de forma:

enum nume {nume0, nume1, . . . , numen};

Prin această declaraţie se defineşte tipul enumerat nume, iar numei are valoarea i. O formă mai generală a declaraţiei de mai sus permite programatorului să forţeze valorile numelor din acoladă. În acest scop, se pot folosi construcţii de forma:

numei= eci

unde eci este o expresie constantă de tip int.Cu alte cuvinte, unui nume i se poate atribui o valoare sau valoarea

lui coincide cu a numelui precedent mărită cu 1. Dacă primului nume din acoladă nu i se atribuie o valoare, el are valoarea 0. Numele nume0, nume1,. . . , numen trebuie să fie nume diferite. Ele sunt constante şi valoarea lor se stabileşte prin declaraţia în care au fost scrise. Domeniul lor de valabilitate este definit de domeniul de valabilitate al declaraţiei prin care se definesc:

instrucţiunea compusă care conţine declaraţia;fişierul sursă în care este scrisă declaraţia, dacă este externă oricărei

funcţii.Valorile atribuite lui nume0, nume1, . . . , numen sunt de obicei diferite,

dar unele pot să şi coincidă.După ce s-a introdus un tip enumerat, se pot declara date de tipul

respectiv printr-o declaraţie de forma:

enum nume lista_de_variabile;

Datele de tip enumerat se consideră de tip int şi se pot utiliza în program oriunde este legal să apară o dată de tip int.

- 60 -

Page 61: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Observaţii:1o. Se pot utiliza, ca şi în cazul structurilor, construcţii de forma:enum nume {nume0, nume1,. . . , numen} lista_de_variabile;sauenum { nume0, nume1,. . . , numen} lista_de_variabile;

2o. De asemenea, se poate utiliza construcţia typedef pentru a atribui un nume unui tip enumerat:

typedef enum nume {nume0, nume1,. . . , numen} NUME;

În continuare se pot declara date de tipul NUME, astfel:

NUME lista_de_variabile;

Exemple:enum luna{ian=1,feb,mar,apr,mai,iun,iul,aug,sep,oct,nov,dec};enum luna luna_calendaristica

Prin prima declaraţie se introduce tipul enumerat luna. Mulţimea de valori asociate acestui tip este formată din numerele întregi 1,2, . . . , 12. Se pot utiliza denumirile:

ian ia valoarea 1feb ia valoarea 2 . . . dec ia valoarea 12

A doua construcţie declară data luna_calendaristica de tipul luna. Ei i se pot atribui valori prin expresii de atribuire de forma:

luna_calendaristica = mar sauluna_calendaristica = mai + 4

typedef enum {luni, marti,miercuri,joi,vineri,sambata,duminica} ZI;ZI z;

Variabila z este de tip ZI. Se poate utiliza în expresii de forma: z=marti;if(z<sambata) // trateaza ziua de lucruelse // trateaza zi de odihna

- 61 -

Page 62: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 9.L I S T E

9.1. DATE STRUCTURATE DEFINITE RECURSIVLimbajul C permite definirea de tipuri structurate recursiv

(autoreferenţiate). Acest lucru se face cu ajutorul pointerilor, şi anume un element al structurii poate să fie un pointer spre tipul de dată introdus prin structura respectivă:

struct nume{ declaratii struct nume *p; declaratii};

Un tip definit ca mai sus se spune că este un tip autoreferit sau recursiv. O dată structurată declarată printr-un astfel de tip se spune că este autoreferită sau recursivă. Datele structurate recursive au numeroase aplicaţii în prelucrarea listelor înlănţuite şi arborescente.

Două tipuri structurate t1 şi t2 pot să conţină fiecare un pointer spre celalalt. În acest caz se va proceda ca mai jos:

struct t1; // o declaratie inainte fara de care nu se poate

// declara tipul t2struct t2

{ declaratii struct t1 *pt1; declaratii

};struct t1

{ declaratii struct t2 *pt2; declaratii

};

9.2. LISTE ÎNLĂNŢUITE- 62 -

Page 63: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Datele structurate se pot organiza în tablouri sau în structuri recursive introducând în tipul structurat unul sau mai mulţi pointeri spre tipul structurat respectiv. Astfel se stabileşte o relaţie de ordine (uneori chiar mai multe) între elementele mulţimii de date structurate; de asemenea, mulţimea rescpectivă se poate organiza în mod dinamic, adăugând elemente noi sau suprimându-le pe cele care nu mai sunt necesare.

Definiţie O mulţime dinamică de structuri recursive de acelaşi tip şi care satisfac una sau mai multe relaţii de ordine introduse prin pointeri se numeşte listă înlănţuită. Elementele listei se mai numesc noduri.

Cele mai utilizate tipuri de listă sunt:lista simplu înlănţuită;lista circulară simplu înlănţuită;lista dublu înlănţuită;lista circulară dublu înlănţuită;.

9.3. LISTA LINIARĂ SIMPLU ÎNLĂNŢUITĂO listă simplu înlănţuită; este o listă înlănţuită; ale cărei noduri

satisfac o singură relaţie de ordine introdusă prin pointeri.Tipul unui nod dintr-o listă simplu înlănţuită; se poate declara în două

moduri:struct tnod{ declaratii struct tnod *urmator; declaratii};typedef struct tnod{ declaratii struct tnod *urmtor; declaratii} TNOD;

Observaţii1o. Varianta b) este mai folosită.2o. Pointerul următor introduce o relaţie de ordine între nodurile de tip

TNOD.3o. Ultimul nod al listei va avea pointerul urmator = NULL.4o. Pentru nodurile interioare ale listei pointerul urmator va avea valori

adrese; dacă urmator din nodul a pointează spre nodul b, spunem că nodul b este succesorul lui a.

Operaţiile ce se pot efectua asupra unei liste simplu înlănţuită;crearea listei;accesul la un nod al listei;inserarea unui nod înlănţuită;ştergerea unui nod dintr-o listă;ştergerea unei liste.

Memorarea listelor se poate face:dinamic (în memoria internă);static (în fişiere).

Pentru modul dinamic se va folosi funcţia malloc la crearea listei cât şi la inserarea de noduri, iar la ştergerea de noduri funcţia free.

9.4. CREAREA ŞI AFIŞAREA UNEI LISTEVom crea o listă ce conţine în noduri informaţii despre numere

întregi şi pătratele lor. Avem două funcţii: una de creare care întoarce adresa capului listei şi o funcţie de afişare a informaţiei din noduri. Vom comenta instrucţiunile importante din program.

#include <stdio.h>#include <alloc.h>typedef struct nod // definirea tipului NOD { int nr;int patrat;

- 63 -

Page 64: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

struct nod* leg; } NOD;

NOD *creaza(void) // functia de creare, intoarce adresa capului { NOD *cap,*p,*pc; int i,lung; printf("\n\n\n\ creare lista simplu inlantuita\n\n"); lung = sizeof(NOD); pc=(NOD *)malloc(lung); // pc este un pointer curent, in el se vor

pune adresel noi cap=pc; printf("dati informatia elementului : "); scanf ("%d",&i); while (i>0) // crearea listei se termina la numar

negativ { p=pc; // pointer ce pastreaza adresa noua p->nr=i; // incarcarea informatiei p->patrat=i*i; pc=(NOD *)malloc(lung); // se cere o noua adresa de memorie p->leg=pc; // se leaga pointerul leg la noua adresa printf("dati informatia elementului : "); scanf ("%d",&i); } p->leg=NULL; // ultimul nod al listei are pointerul leg =

NULL free(pc); // eliberarea ultimei adrese care de fapt nu face

parte din lista return cap; // returneaza adresa capului listei }

void afisare(NOD *p) // functia de afisare a listei{ while (p != NULL) // cat timp n-am ajuns la ultimul nod { printf ("\n numarul %d si patratul sau %d", p->nr,p->patrat); p=p->leg; // trecerea la urmatorul nod al listei }}

void main (void) // functia principala{ NOD *capul; clrscr(); capul=creaza(); // lista e cunoscuta prin adresa capului afisare(capul); getch();}

- 64 -

Page 65: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 10.PRELUCRAREA FIŞIERELOR

10.1. FIŞIEREÎn general, prin fişier înţelegem o colecţie ordonată de elemente numite

înregistrări, care sunt păstrate pe diferite suporturi de memorie externă. Suportul de memorie externă cel mai folosit este suportul magnetic (de obicei discuri sub forma de flopy şi hardiscuri sau bandă magnetică care e din ce în ce mai rar folosită). Suportul magnetic este reutilizabil deoarece zona utilizată pentru a păstra înregistrările unui fişier poate fi ulterior reutilizată pentru a păstra înregistrările altui fişier.

Datele introduse de la un terminal se consideră că formează un fişier de intrare. Înregistrarea în acest caz, de obicei, este formată din datele tastate la terminal pe un rând deci caracterul de rând nou (newline) este terminator de înregistrare. În mod analog, datele care se afişează pe terminal formează un fişier de ieşire. Înregistrarea poate fi formată din caracterele unui rând.

Un fişier are o înregistrare care marchează sfârşitul de fişier. În cazul fişierelor de intrare de la tastatură sfârşitul de fişier se generează prin:

CTRL/ZEl poate fi pus în evidenţă folosind constanta simbolică EOF definită în

fişierul stdio.h.Prelucrarea fişierelor implică un număr de operaţii specifice acestora.

Două operaţii sunt absolut necesare la prelucrarea oricărui fişier:deschiderea fişierului;închiderea fişierului.Aceste operaţii de deschidere şi închidere a unui fişier se pot realiza

prin intermediul unor funcţii speciale din biblioteca standard a limbajului C. Alte operaţii privind prelucrarea fişierelor sunt:

crearea unui fişier;consultarea unui fişier;actualizarea unui fişier;adăugarea de înregistrări într-un fişier;poziţionarea într-un fişier;ştergerea unui fişier.

Prelucrarea fişierelor se poate face la două nivele. Primul nivel face apel direct la sistemul de operare şi se numeşte nivelul inferior de prelucrare al fişierelor. Cel de-al doilea nivel de prelucrare se realizează prin utilizarea unor proceduri specializate în prelucrarea fişierelor care, printre altele, pot rezerva şi gestiona automat zone tampon necesare realizării operaţiilor de intrare/ieşire, şi se numeşte nivelul superior de prelucrare al fişierelor

10.2. NIVELUL INFERIOR DE PRELUCRARE AL FIŞIERELORLa acest nivel de prelucrare se folosesc 5 funcţii:open (creat)- pentru deschiderea fişierelor;read - pentru citirea din fişier;write - pentru citirea din fişier;lseek - pentru poziţionarea în fişier;close - pentru închiderea fişierului.

10.2.1. Deschiderea unui fişierOrice fişier înainte de a fi prelucrat trebuie deschis. Această operaţie

se realizează prin intermediul funcţiei open al cărui prototip este următorul:int open (const char *cale, int acces);unde:cale este un pointer spre un şir de caractere care defineşte calea spre

fişierul care se deschide (în cea mai simplă formă este numele fişierului dacă se află în directorul curent)

acces este o variabilă de tip întreg care poate lua una din valorile:- O_RDONLY - fişierul se deschide numai în citire (consultare);- O_WRONLY - fişierul se deschide numai în scriere (creare); (sau O_CREAT)- O_RDWR - fişierul se deschide în citire/scriere;

- 65 -

Page 66: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- O_APPEND - fişierul se deschide la sfârşit pentru adăugare;- O_BINARY - fişierul se prelucrează binar;- O_TEXT - fişierul este de tip text.

Unele valori din cele de mai sus se pot combina cu ajutorul operatorului |. De exemplu O_RDWR | O_BINARY pentru deschiderea fişierului în scriere/citire binară.

Observaţii:1o. Funcţia open întoarce descriptorul de fişier care este o valoare

intreagă ce va identifica fişierul în toate celelate operaţii care se vor realiza asupra lui. Dacă deschiderea unui fişier nu reuşeşte (de obicei unul din parametrii este eronat) atunci funcţia open returnează valoarea –1.

2o. Pentru a putea utiliza funcţia open trebuie incluse fişierele header io.h şi fcntl.h.

3o. Pentru a crea un fişier nou se va folosi funcţia creat în locul funcţiei open cu prototipul:

int creat (const char *cale, int mod); unde:- cale are aceeaşi semnificaţie ca şi la funcţia open;- mod este un întreg care poate fi definit prin constantele simbolice de

mai jos:S_IREAD - se poate citi fişierul;S_IWRITE - se poate scrie în fişier;S_IEXEC - se poate executa programul conţinut în fişier.

Utilizarea funcţiei presupune includerea fişierelor io.h şi stat.h4o. Implicit fişierul se consideră că este de tip text.

Exemple:char nume_fisier[ ]=”fis1.dat”;int df;df = open (nume_fisier, O_RDONLY);Prin apelul de mai sus se deschide în citire fişierul fis1.dat din

directorul curent.

int df;df = open (“c:\\borlandc\\help.txt”,O_APPEND);Se deschide în adăugare fişierul help.txt din directorul borlandc de pe

discul C.

10.2.2. Citirea dintr-un fişier (consultare)Funcţia folosită pentru operaţia de citire dintr-un fişier în memorie se

numeşte read şi are prototipul următor:int read (int df, void *buf, unsigned lung);

unde:- df este descriptorul de fişier a cărui valoare a fost definită la

deschidere;- buf este pointerul spre zona de memorie în care se recepţionează

înregistrarea care se citeşte;- lung este lungimea în octeţi a inregistrării citite.

Observaţii:1o. La fiecare apel funcţia returnează înregistrarea curentă. La primul

apel se citeşte prima înregistrare din fişier, la al doilea apel se citeşte a doua, etc. Ordinea înregistrărilor în fişier este cea definită la crearea fişierului.

2o. La un apel al funcţiei read se citesc cel mult lung octeţi înregistrarea având definită lungimea la scrierea în fişier. Funcţia reîntoarce numărul de octeţi citiţi, 0(zero) la sfârşit de fişier, sau –1 la eroare. De obicei se folosesc frecvent înregistrări de 512 octeţi sau chiar mai mari.

3o. Funcţia read poate fi folosită pentru a citi de la intrarea standard. În acest caz, descriptorul de fişier este 0 (stdin are 0, stdout are 1, stderr are 2 stdprn are 3 stdaux are 4). Programatorul nu trebuie să deschidă fişierele standard deoarece ele sunt deschise automat la lansarea în execuţie a programului.

4o. Utilizarea funcţiei read, presupune includerea fişierului io.h.- 66 -

Page 67: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

10.2.3. Scrierea într-un fişier (creare, actualizare, adăugare)Pentru a scrie într-un fişier se foloseşte funcţia write. Se presupune că

fişierul este deschis în prealabil prin funcţia creat sau open. Ea este asemănătoare cu funcţia read, doar că realizează transferul invers, adică din memorie în fişier şi are prototipul:

int write (int df, void *buf, unsigned lung);Observaţii:1o. Funcţia returnează numărul octeţilor scrişi în fişier. Acesta este

egal cu lung şi defineşte lungimea înregistrării scrise în fişier. În cazul în care numărul returnat de funcţia write diferă de parametrul lung scrierea a fost eronată şi se reîntoarce valoarea –1.

2o. Utilizarea funcţiei write implică includerea fişierlui io.h.

10.2.4. Poziţionarea într-un fişierPentru a avea acces aleator la înregistrările unui fişier se foloseşte o

funcţie de poziţionare în fişier pe anumite înregistrări dorite. Pe fişierele care au suporturi magnetice este posibilă poziţionarea cu ajutorul funcţiei lseek care are prototipul următor:

long lseek (int df, long deplasament, int origine)unde:- df este descriptorul de fişier;- deplasament defineşte numărul de octeţi peste care se va deplasa capul

de scriere/citire al discului;- origine are una din valorile: 0 deplasamentul se consideră de la începutul fişierului; 1 deplasamentul se consideră din poziţia curentă a capului de scriere/citire; 2 deplasamentul se consideră de la sfârşitul fişierului.

Observaţii:1o. Funcţia returnează poziţia capului de citire/scriere faţă de începutul

fişierului în număr de octeţi sau –1L la eroare.2o. Funcţia nu realizează nici un transfer de informaţie ci doar

poziţionează capul de citire/scriere în fişier. Deci pentru transfer e nevoie de funcţiile read sau write.

3o. Utilizarea funcţiei presupune includerea fişierului io.h.4o. Apelul lseek (df, 0L, 0) permite o poziţionare la început de fişier iar apelul lseek (df, 0L, 2) permite o poziţionare la sfârşit de fişier.

10.2.5. Închiderea unui fişierLa sfârşitul prelucrării unui fişier acesta trebuie închis. Acest lucru se

realizează automat dacă programul se termină prin apelul funcţiei exit. Programatorul poate închide un fişier folosind funcţia close. Se recomandă închiderea unui fişier de îndată ce s-a terminat prelucrarea lui. Aceasta din cauză că numărul fişierelor ce pot fi deschise simultan este limitat. Limita este dependentă de sistemul de operare şi ea variază, de obicei, în intervalul 15-25. De obicei numărul de buffere (zone tampon) asociate fişierelor se precizează în fişierul autoexec.bat. Menţionăm că fişierele standard din limbajul C nu se închid de programator.

Funcţia close are prototipul următor:int close (int df);

unde df este descriptorul fişierului care se închide.

Observaţii:1o. La o închidere normală, funcţia returnează valoarea 0 şi –1 în caz de

incident.2o. Utilizarea funcţiei close implică includerea fişierului io.h.ExempleVom deschide fişierul “fis1.dat” în creare şi vom scrie în el două

înregistrări.

- 67 -

Page 68: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

#include<io.h>#include<fcntl.h>void main (void){ int df,i; df = open("fis1.dat", O_CREAT);// se deschide fisierul fis1.dat in creare if (df != -1) // se testeaza daca fiserul s-a deschis corect { write (df,"cioban vasyle\n", 14);// se scriu doua inregistrari write (df,"cioban andrei\n", 14); } else printf (“nu s-a deschis fisierul); close (df); // se inchide fisierul}

Se afişează conţinutul fişierului “fis1.dat”#include<io.h>#include<fcntl.h>#include <stdio.h>#include <conio.h>void main (void){int df; char s[14]; df = open("fis1.dat", O_RDONLY); // se deschide fisierul în citire if (df != -1) // se testeaza daca deschiderea e corecta { read (df, s, 14); printf ("%.14s",s); read (df, s, 14); printf ("%.14s",s); } else printf (“nu s-a deschis fisierul); close (df); getch(); // se asteapta un caracter de la tastatura}programul următor copiază intrarea standard la ieşierea standard folosind

o zonă tampon de 80 de caractere:

#define LZT 80#include <io.h>void main (void) // copiaza intrarea standard la iesirea standard{ char zona_tampon[LZT]; int i; while ((i = read (0, zona_tampon, LZT)) > 0) write (1, zt, i);}

10.3. NIVELUL SUPERIOR DE PRELUCRARE A FIŞIERELORDupă cum am amintit, la acest nivel fişierele se prelucrează cu ajutorul

unor proceduri specializate.

Deschiderea unui fişierFuncţia fopen se utilizează pentru deschiderea unui fişier. Ea returnează

un pointer spre tipul FILE (tipul fişier), tip definit în fişierul stdio.h. Tipul FILE este un tip structurat şi el depinde de sistemul de operare. În caz de eroare, funcţia fopen returnează pointerul NULL. Prototipul funcţiei fopen este următorul:

FILE *fopen (const char *cale, const char *mod);unde:cale are aceeaşi semnificaţie ca şi în cazul funcţiilor open şi creat.mod este un pointer spre un şir de caractere care defineşte modul de

prelucrare al fişierului după deschidere. Acest şir de caractere se defineşte în felul următor:

- “r” - deschidere în citire (read);- “w” - deschidere în scriere (write);- “a” - deschidere pentru adăugare;- “r+” - deschidere pentru modificare (citire sau scriere);- “rb” - citire binară;- “wb” - scriere binară;- “r+b” - citire/scriere binară.

- 68 -

Page 69: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Observaţii:1o. Dacă se deschide un fişier inexistent cu modul “w” sau “a”, atunci el

este deschis în creare.2o. Dacă se deschide un fişier existent cu modul “w”, atunci conţinutul

vechi al fişierului se pierde şi se va crea unul nou cu acelaşi nume.3o. Menţionăm că, stdin, stdout, stderr, stdaux şi stdprn sunt pointeri

spre tipul FILE şi permit ca funcţiile de nivel superior de prelucrare a fişierelor să poată trata intrarea standard şi ieşirile standard pe terminal şi imprimantă la fel ca şi fişierele pe celelalte suporturi. Singura deosebire constă în aceea că aceste fişiere nu se deschid şi nici nu se închid de către programator. Ele sunt deschise automat la lansarea în execuţie a programului şi se închid la apelul funcţiei exit.

4o. Apelul funcţiei se realizează prin construcţia:FILE *pf;pf = fopen (“FIS1.DAT”,”w”);

10.3.2. Prelucrarea pe caractere a unui fişierFişierele pot fi scrise şi citite caracter cu caracter, folosind două

funcţii simple: putc pentru scriere;getc pentru citire.Funcţia putc are prototipul:

int putc (int c, FILE *pf);unde:c este codul ASCII al caracterului care se scrie în fişier;pf este pointerul spre tipul FILE a cărui valoare a fost returnată de

funcţia fopen la deschiderea fişierului în care se scrie; pf poate fi şi stdout, sdterr, stdaux, stdprn.

Funcţia putc returnează valoarea lui c respectiv –1 în caz de eroare.Funcţia getc are prototipul:

int getc (FILE *pf);unde:pf este pointerul spre tipul FILE a cărui valoare a fost returnată de

funcţia fopen la deschiderea fişierului; în particular pf poate fi stdin.Funcţia getc returnează codul ASCII al caracterului citit sau EOF la

sfârşit de fişier sau eroare.

Închiderea unui fişier se realizează cu ajutorul funcţiei fclose care are prototipul:

int fclose (FILE *pf);unde:pf este pointerul spre tipul FILE a cărui valoare a fost definită la

deschiderea fişierului prin intermediul funcţiei fopen.

Funcţia fclose returnează:0 la închiderea normală a fişierului;1 în caz de eroare.Exemple:Programul următor copiază intrarea standard la ieşirea standard stdout,

folosind funcţiile getc şi putc.

#include <stdio.h>void main (void){ int c; while (( c = getc (stdin)) != EOF) putc (c, stdout);}Programul următor copiază intrarea standard la imprimantă.

#include <stdio.h>void main (void){ int c; while (( c = getc (stdin)) != EOF) putc (c, stdprn);}

- 69 -

Page 70: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Programul următor scrie la ieşirea stdout caracterele unui fişier a cărui cale este argumentul din linia de comandă. Dacă nu există un argument în linia de comandă, atunci se citeşte de la intrarea standard.

#include <stdio.h>void main (int argc, char *argv[ ] ){ FILE *pf; int c; if (argc = = 1)

pf = stdin; // nu exista argument in linia de comanda else // se deschide fisierul a carui cale se afla in argv[1]

if (( pf = fopen (*++argv,”r”)) = = NULL){ printf (“nu se poate deschide fisierul %s\n”,*argv); exit (1);

} while (( c = getc (pf)) != EOF) putchar(c); exit (0); }

Operaţiile de intrare-ieşire cu formatBiblioteca standard a limbajului C conţine funcţii care permit realizarea

operaţiilor de intrare/ieşire cu format. Astfel se pot utiliza funcţiile fscanf şi fprintf, prima pentru citire cu format dintr-un fişier, iar a doua pentru scriere cu format într-un fişier.

Funcţia fscanf este asemănătoare cu funcţia scanf. Ea are un parametru în plus faţă de scanf. Acest parametru este un pointer spre tipul FILE şi el defineşte fişierul din care se face citirea. Acest pointer este primul parametru al funcţiei fscanf. Funcţia poate fi apelată printr-o expresie de atribuire de forma:

nr = fscanf (pf, control, lista_de_parametrii );unde :pf este un pointer spre tipul FILE şi valoarea lui a fost definită prin

apelul funcţiei fopen; defineşte fişierul din care se face citirea;ceilalţi parametri sunt identici cu cei utilizaţi la apelul funcţiei

scanf.Funcţia fscanf, ca şi funcţia scanf, returnează numărul câmpurilor citite

din fişier. La întâlnirea sfârşitului de fişier se returnează valoarea EOF definită în fişierul stdio.h. Pentru pf = stdin, funcţia fscanf este identică cu scanf.

Funcţia fprintf , ca şi funcţia printf, returnează numărul caracterelor scrise în fişier sau –1 în caz de eroare. Pentru pf = stdout, funcţia fprintf este identică cu printf. De asemenea, se pot utiliza pointerii standard obisnuiţi: stderr, stdaux, stdprn.

Exemplu:Vom scrie un program cu ajutorul căruia se va crea un fişier cu numele

"FIS.DAT" şi care conţine înregistrări cu numele, prenumele şi adresa unor persoane.

#include <stdio.h>void main(void){ int n=0, i=1;// n este numarul de înregistrari ce se va scrie in fisier char nume[25], prenume[30], adresa[50]; FILE *pf; printf("\n Dati numarul de inregistrari n= "); scanf("%d",&n); pf=fopen("FIS.DAT","w"); if (pf = = NULL) { printf ("Eroare la deschidere"); return; } do { printf("\n Nume : "); scanf("%s",nume); printf("\n Prenume : ");

- 70 -

Page 71: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

scanf("%s",prenume); printf("\n Adresa : "); scanf("%s",adresa); fprintf(pf,"%s %s %s", nume, prenume, adresa); i++; } while (i<=n); fclose(pf);}

10.3. 5. Intrări-ieşiri de şiruri de caractereBiblioteca standard a limbajului C conţine funcţiile fgets şi fputs care

permit citirea respectiv scrierea într-un fişier ale cărui înregistrări sunt şiruri de caractere.

Funcţia fgets are prototipul:char *fgets (char *s, int n, FILE *pf);

unde:s este pointerul spre zona în care se face citirea caracterelor;n-1 este numărul maxim de caractere care se citesc;pf este pointerul spre tipul FILE care defineşte fişierul din care se face

citirea.De obicei s este numele unui tablou de tip char de dimensiune cel puţin n.

Şirul se termină cu ‘\0’ (caracterul NUL). La întâlnirea caracterului ‘\n’, citirea se opreşte. În acest caz, în zona receptoare se transferă caracterul ‘\n’ şi apoi caracterul NUL (‘\0’).

În mod normal, funcţia returnează valoarea pointerului s. La întâlnirea sfârşitului de fişier se returnează valoarea NULL.

Funcţia fputs scrie într-un fişier un şir de caractere care se termină prin ‘\0’. Ea are prototipul:

int fputs (const char *s, FILE *pf);unde: s este pointerul spre zona care conţine şirul de caractere care se scrie;pf este pointerul spre zona care conţine şirul de caractere care se scrie.Funcţia fputs returnează codul ASCII al ultimului caracter scris sau –1 în

caz de eroare. Aceste funcţii sunt realizate folosind funcţia getc pentru fgets şi putc

pentru fputs.Pentru a citi de la intrarea standard stdin, se poate folosi funcţia gets,

care nu mai are parametrii pf şi n. Parametrul pf este implicit stdin. Funcţia gets citeşte caracterele de la intrarea standard până la întâlnirea caracterului ‘\n’ care nu mai este păstrat în zona spre care pointează s. Şirul de caractere citit se termină şi în acest caz prin ‘\0’.

În mod analog, pentru a scrie la ieşirea standard stdout se poate folosi funcţia puts, care nu mai are parametrul pf, acesta fiind implicit stdout. În rest, funcţia puts este la fel ca şi funcţia fputs.

10.3.6. Poziţionarea într-un fişierCu ajutorul funcţiei fseek se poate deplasa capul de citire/scriere al

discului în vederea prelucrării înregistrărilor fişierului într-o ordine oarecare, diferită de cea secvenţială (acces aleator). Această funcţie este asemănătoare cu funcţia lseek. Ea are prototipul următor:

int fseek (FILE *pf, long deplasament, int origine);unde:pf este pointerul spre tipul FILE care defineşte fişierul în care se face

poziţionarea capului de citire/scriere;deplasament şi origine au aceeaşi semnificaţie ca şi în cazul funcţiei

lseek.Funcţia fseek returnează valoarea zero la poziţionare corectă şi o valoare

diferită de zero în caz de eroare.Funcţia ftell indică poziţia capului de citire în fişier. Ea are

prototipul:long ftell (FILE *pf);

unde:pf este pointerul spre tipul FILE care defineşte fişierul în cauză.

- 71 -

Page 72: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Funcţia returnează o valoare de tip long care defineşte poziţia curentă a capului de citire/scriere, şi anume reprezintă deplasamentul în octeţi a poziţiei capului faţă de începutul fişierului.

10.3.7. Prelucrarea fişierelor binareFişierele organizate ca date binare (octeţii nu sunt consideraţi ca fiind

coduri de caractere) pot fi prelucrate la acest nivel folosind funcţiile fread şi fwrite. În acest caz se consideră că înregistrarea este o colecţie de date structurate numite articole. La o citire, se transferă într-o zonă specială, numită zonă tampon, un număr de articole care se presupune că au o lungime fixă. În mod analog, la scriere se transferă din zona tampon un număr de articole de lungime fixă. Cele două funcţii au prototipurile de mai jos:

unsigned fread (void *ptr, unsigned dim, unsigned nrart, FILE *pf);unde:- ptr este pointerul spre zona tampon ce conţine articolele citite

(înregistrarea citită);- dim este un întreg ce reprezintă lungimea unui articol;- nrart este un întreg ce reprezintă numărul articolelor care se

transferă;- pf este un pointer spre tipul FILE care defineşte fişierul din care se

face citirea.

unsigned fwrite (const void *ptr, unsigned dim, unsigned nrart, FILE *pf);

Parametrii funcţiei fwrite au aceeaşi semnificaţie ca şi parametrii funcţiei fread.

Ambele funcţii returnează numărul articolelor transferate sau –1 în caz de eroare.

Exemplu:Programul următor citeşte de la intrarea standard stdin datele ale căror

formate sunt definite mai jos şi le scrie în fişierul miscari.dat din directorul curent.

tip denumire um cod pret cantitate1 TELVIZOR buc 0001 3000000 200

#include <stdio.h>#define MAX 60typedef struct { char tip[2];

char den[MAX];int val;char unit[3];long cod;float pret;float cant;

} ARTICOL;

// 6 articole in zona tampon union {

ARTICOL a[6];char zt[6*sizeof(ARTICOL)];

}buf;int cit (ARTICOL *str) // citeste datele de la intrarea standard si { // le scrie in structura spre care pointeaza str int nr,c; float x,y; while ((nr = scanf("%1s %60s %3d %2s %ld", str -> tip, str ->den,

&str ->val, str -> unit,&str -> cod)) != 5|| scanf("%f %f", &x,&y) != 2)

{ if(nr = = EOF) return (EOF); printf ("rind eronat ; se reia\n");// avans pina la newline while ((c = getchar ()) != '\n' && c != EOF); if (c == EOF) return (EOF);

- 72 -

Page 73: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

} // sfarsit whilestr -> pret = x;str -> cant = y;return (nr);} // sfarsit cit

void main (void) // creaza fisierul miscari.dat cu datele citite de la intrarea standard

{ FILE *pf; ARTICOL a; int i,n; if((pf = fopen("miscari.dat", "wb")) == NULL) {printf("nu se poate deschide in creare fisierul miscari.dat\n");exit(1);}for ( ; ; ){ // se umple zona tampon a fisierului for (i = 0 ;i<6 ; i++) { if((n=cit(&a)) == EOF) break; buf.a[i]=a; }// se scrie zona tampon daca nu este vida if(i !=0 ) if(i!=fwrite(buf.zt, sizeof(ARTICOL), i, pf)) { printf("eroare la scrierea in fisier\n"); exit(1); } if(n = = EOF) break; } fclose(pf);}

10.4. ŞTERGEREA UNUI FIŞIERUn fişier poate fi şters apelând funcţia unlink. Aceasta are prototipul:

int unlink (const char *cale);unde:- cale este un pointer spre un şir de caractere identic cu cel

utilizat la crearea fişierului în funcţia creat sau fopen.Funcţia unlink returnează valoarea zero la o ştergere reuşită,

respectiv -1 în caz de eroare.

- 73 -

Page 74: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 11.FUNCŢII STANDARD

Vom descrie câteva clase de funcţii, numite clase de funcţii standard aflate în bibliotecile mediului BORLAND C. Funcţiile şi macrourile utilizate frecvent în majoritatea aplicaţiilor se pot grupa în următoarele clase:

funcţii de prelucrare a fişierelor;funcţii de alocare dinamică a memoriei;macrouri de clasificare;macrouri de transformare a simbolurilor;funcţii care realizează conversii;funcţii de prelucrare a şirurilor de caractere;funcţii de calcul;funcţii pentru controlul proceselor;funcţii de gestiune a datei şi a orei;funcţii de gestiune a ecranului.

Funcţiile de la punctele a) şi b) au fost descrise în lecţii anterioare.

11.1. MACROURI DE CLASIFICAREÎn această clasă distingem un număr de macrouri simple care au o utilizare

largă în prelucrarea simbolurilor. Definiţiile acestor macrouri se află în fişierul ctype.h

Unul dintre macrouri este denumit isascii şi are prototipul:int isascii (int c);

Macroul returnează o valoare diferită de zero dacă valoarea lui c aparţine intervalului de numere întregi [0,127] şi zero în caz contrar. Acest macrou permite să se testeze dacă valoarea parametrului său reprezintă un cod ASCII sau nu.

Celelalte macrouri au prototipul următor:int nume (int c);unde nume este unul din următoarele:isalpha dacă c este codul unei litere;isalnum dacă c este codul unei litere sau cifre;isdigit dacă c este codul unei cifre;isgraph dacă c este codul unui caracter imprimabil inclusiv spaţiul;islower dacă c este codul unei litere mici;isprint dacă c este codul unui caracter imprimabil inclusiv spaţiu;isspace dacă c reprezintă spaţiu, tabulator, retur de car, rând nou,

tabulator vertical, salt la început de pagină de imprimantă.isupper dacă c este codul unei litere mari;isxdigit dacă c este codul unei cifre hexazecimale.

Exemplu:Programul următor citeşte un fişier şi îl rescrie schimbând literele mari

cu litere mici. Căile spre cele două fişiere (sursă şi destinaţie) sunt argumente în linia de comandă:

argv[1] este un pointer spre fişierul sursă;argv[2] este un pointer spre fişierul destinaţie.

#include <stdio.h>#include <ctype.h>void main ( int argc, char *argv[ ] ){ FILE *pf1, *pf2; int c; if (argc != 3)

{ printf (“linia de comanda eronata\n”); exit(1);}

if (( pf1 = fopen (argv[1],”r”) ) = = NULL{ printf (“nu se poate deschide fisierul”%s\n”, argv[1]); exit(1);}

- 74 -

Page 75: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

if (( pf2 = fopen (argv[2],”w”) ) = = NULL{ printf (“nu se poate deschide fisierul”%s\n”, argv[2]); exit(1);}

while (( c = getc (pf1)) != EOF)if (isascii(c) && isupper (c))

putc (c - ‘A’ + ‘a’, pf2); // c este codul unei litere mari

elseputc(c, pf2); // c este codul unei litere mici

fclose (pf1); fclose (pf2);

// afisarea fisierului destinatie if (( pf2 = fopen (argv[2], “r”) = = NULL)

{ printf (“nu se poate deschide in citire:%s\n”, argv[2]); exit(1);}

while ((c = getc(pf2)) != EOF) putchar (c); fclose (pf2);}

11.2. MACROURI DE TRANSFORMARE A SIMBOLURILORÎn această clasă distingem macrouri definite tot în fişierul ctype.h.

Prototipurile acestor macrouri sunt:int toascii (int c); returnează ultimii 7 biţi ai lui c (care

reprezintă un cod ASCII);int tolower (int c); transformă pe c din literă mare în literă mică;int toupper (int c); transformă pe c din literă mică în literă mare.

11.3. CONVERSIIO dată are un format extern şi un format intern. Prin conversie înţelegem

o transformare a unei date dintr-un format al ei în celălalt. Conversiile se pot face sub controlul unui format sau fără format. Dintre funcţiile care realizează conversii sub controlul formatelor amintim:

printf; fprintf; scanf; fscanf;Aceste funcţii au fost descrise în lecţiile anterioare. Vom da în

continuare câteva funcţii care realizează conversii fără format şi care sunt utilizate mai frecvent. Aceste funcţii au prototipurile în fişierul stdlib.h.

Funcţia atoi are prototipul:int atoi (const char *ptr);

unde:ptr este un pointer spre o zonă de tip caracter ce conţine cifre zecimale

care sunt, eventual, precedate de semnul minus; Efectul:şirul de cifre spre care pointează ptr este convertit din întreg zecimal

în întreg binar de tip int.Observaţie:1o. Funcţia returnează rezultatul acestei conversii.

Funcţia atol are prototipul:long atol (const char *ptr);

unde:ptr este un pointer spre o zonă de tip caracter ce conţine cifre zecimale

care sunt, eventual, precedate de semnul minus; Efectul:şirul de cifre spre care pointează ptr este convertit din întreg zecimal

în întreg binar de tip long.Observaţie:1o. Funcţia returnează rezultatul acestei conversii.

Funcţia atof are prototipul:double atof (const char *ptr);

unde:ptr este un pointer spre o zonă de tip caracter ce conţine cifre zecimale

care sunt, eventual, precedate de semnul minus (poate conţine marca zecimală); - 75 -

Page 76: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Efectul:şirul de cifre spre care pointează ptr este convertit în virgulă flotantă

dublă precizie.Observaţie:1o. Funcţia returnează rezultatul acestei conversii.

Funcţia itoa are prototipul:char *itoa (int val, char *sir, int baza)

Efectul:valoarea parametrului val se converteşte din întreg binar de tip int în

baza de numeraţie definită de parametrul baza şi se păstrează în zona spre care pointează sir.

Observaţie:1o. Funcţia returnează pointerul sir.

Funcţia ltoa are prototipul:char *ltoa (long val, char *sir, int baza)

Efectul:valoarea parametrului val se converteşte din întreg binar de tip long în

baza de numeraţie definită de parametrul baza şi se păstrează în zona spre care pointează sir.

Observaţie:1o. Funcţia returnează pointerul sir.

11.4. FUNCŢII DE PRELUCRARE A ŞIRURILOR DE CARACTEREFuncţiile din această clasă implică includerea fişierului string.h.

Indicăm mai jos funcţiile din această clasă, utilizate mai frecvent. O parte din aceste funcţii au mai fost utilizate în diferite exemple din lecţiile anterioare.

Funcţii de copiere:char *strcpy (char *dest, const char *sursa);

char *strncpy (char *dest, const char *sursa, unsigned n);

prima funcţie copiază şirul de caractere spre care pointează sursa în zona spre care pointează dest;

a doua funcţie realizează acelaşi lucru, dar copiază cel mult primii n octeţi din sursă;

ambele funcţii returnează valoarea pointerului dest.

Funcţii de concatenare:int strcmp (const char *dest, const char *sursa);

char *strncat (const char *dest, const char *sursa, unsigned n);prima funcţie copiază şirul spre care pointează sursa la sfârşitul şirului

din zona spre care pointează dest;a doua funcţie realizează acelaşi lucru, dar se copiază cel mult primii n

octeţi din zona spre care pointează sursa;ambele funcţii returnează valoarea pointerului dest.

Funcţii de comparare:int strcmp (const char *sir1, const char *sir2);int stricmp (const char *sir1, const char *sir2);int strncmp (const char *sir1, const char *sir2, unsigned n);int strnicmp (const char *sir1, const char *sir2, unsigned n);

aceste funcţii compară şirurile de caractere din zonele spre care pointează pointerii sir1 şi sir2;

ele returnează o valoare:negativă, dacă şirul spre care pointează sir1 este mai mic decât cel spre

care pointează sir2;zero, dacă cele două şiruri sunt egale;pozitivă, dacă şirul spre care pointează sir1, este mai mare decât cel

spre care pointează sir2;- 76 -

Page 77: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

prezenţa literei i (ignore) în numele funcţiei înseamnă că nu se face distincţie între literele mari şi mici;

prezenţa literei n în numele funcţiei înseamnă că se realizează comparaţia pe cel mult n octeţi.

Observaţie:1o. Fie şirurile s1 şi s2 de lungime l1 şi l2. Atunci cele două şiruri sunt

egale dacă:l1=l2 ( au aceeaşi lungime);s1[k] = s2 [k] pentru k=0,1,...,l12o. Şirul s1 este mai mic decât şirul s2 dacă există un j, j ≥ 0 şi j ≤ min

(l1, l2), astfel încât:s1[j] < s2[j];s1[k] = s2[k], pentru k=0,1, . . . , j-1.

3o. Şirul s1 este mai mare decât şirul s2 dacă există un j, j ≥ 0 şi j ≤ (l1, l2), astfel incât:

s1[j] > s2[j];s1[k] = s2[k], pentru k=0,1, . . . , j-1.

Funcţia lungime:

unsigned strlen (const char *sir);returnează lungimea şirului de caractere spre care pointează sir;caracterul NUL care termină şirul nu este numărat.

11.5. FUNCŢII DE CALCULMajoritatea funcţiilor matematice au prototipurile în fişierul math.h.

Multe dintre acestea se utilizează la calculul valorilor funcţiilor elementare şi au prototipul:

double nume (double x);unde nume este unul din următoarele:

acos -arccos;asin -arcsin;atan -arctg;cos -cos;sin -sin;exp -ex;log -ln;log10 -lg;sqrt -rădăcina pătrată;

ceil -returnează cel mai mare întreg mai mare sau egal cu x (partea întreagă);

floor -returnează cel mai mare întreg mai mic sau egal cu x;fabs -valoarea absolută;sinh -sinus hiperbolic;cosh -cosinus hiperbolic;tanh -tangentă hiperbolică;

Alte funcţii:double atan2 (double y, double x); - returnează arctg(y/x);double pow (double x, double y); - returnează xy;double cabs (struct complex z); - returnează modulul nr. complex;double poly (double x, int n, double c[ ] ) - returnează valoarea

polinomului de grad n în punctul x, coeficienţii sunt c[0], . . . c[n].

Funcţiile care urmează au prototipul în fişierele stdlib.h şi math.h:int abs (int n); - returnează valoarea absolută din întregul n;long labs (long n); - returnează valoarea absolută din întregul long n;

11.6. FUNCŢII PENTRU CONTROLUL PROCESELORAceste funcţii au prototipurile în fişierul stdlib.h şi în process.h şi

realizează controale asupra programelor:void abort (void); - termină un program în caz de eroare;void exit (int stare) - termină un program şi returnează o stare;

stare este 0 pentru terminare normală şi diferită de zero pentru o terminare anormală; videază buferele fisierelor, închide toate fişierele;

int system (const char *comanda) - execută o comandă DOS definită prin sirul de caractere spre care pointează comanda; returnează 0 la succes si –1 la eroare. Aceste funcţii au prototipurile în stdlib.h şi în process.h.

- 77 -

Page 78: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

11.7. FUNCŢII PENTRU GESTIUNEA DATEI ŞI OREIDăm mai jos prototipurile a patru funcţii pentru citirea/setarea datei şi

orei. Ele implică includerea fişierului dos.h.

void getdate(struct date *d); - încarcă structura de tip date spre care pointează d cu datele corespunzătoare furnizate de sistemul de operare;

void gettime(struct time *t); - încarcă structura de tip time spre care pointează t cu datele corespunzătoare furnizate de sistemul de operare;

void setdate (struct date *t); - setează data curentă în conformitate cu datele de tip date;

void settime (struct time *t); - setează ora curentă în conformitate cu datele de tip time spre care pointează t.

Tipurile date şi time sunt definite în fişierul dos.h. astfel:struct date {

int da_year;int da_day;int da_mon;

};struct time {

unsigned char ti_min;unsigned char ti_hour;unsigned char ti_hund;unsigned char ti_sec;

};Exemplu:void main (void) // afiseaza data si ora{ struct date d;

struct time t;getdate (&d);gettime (&t);printf (“\n\t%02d/%02d/%04d”,d.da_day, d.da_mon, d.da_year);printf (“\tora %02d:%02:%02\n”, t.ti_hour, t.ti_min, t.ti_sec);

}

11.8. ALTE FUNCŢII DIVERSE DE UZ GENERALvoid clrscr (void); - şterge fereastra activă sau tot ecranul;

prototipul în conio.hvoid delay(unsigned i); - suspendă execuţia programului pentru o perioadă

de i milisecunde;void sleep(unsigned i); - suspendă execuţia programului pentru o perioadă

de i secunde;void nosound (void); - opreşte difuzorul calculatorului;void sound(unsigned h); - porneşte difuzorul calculatorului cu un ton egal

cu h Hz.

LECŢIA 12.- 78 -

Page 79: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

GESTIUNEA ECRANULUI ÎN MOD TEXTBiblioteca standard a limbajelor C şi C++ conţine funcţii pentru gestiunea

ecranului. Acesta poate fi gestionat în 2 moduri:modul text şimodul grafic.

Modul text presupune că ecranul este format dintr-un număr de linii şi coloane. De obicei există două variante:

25 de linii x 80 de coloane = 2000 de carctere sau25 de linii x 40 de coloane = 1000 de caractere.

Poziţia pe ecran a unui caracter se defineşte printr-un sistem de coordonate întregi (x,y)

unde:x - reprezintă numărul coloanei în care este situat caracterul;y - reprezintă numărul liniei în care este situat caracterul.

Colţul din stânga sus are coordonatele (1,1) iar colţul din dreapta jos (80,25) sau (40,25).

În mod implicit funcţiile de gestiune a ecranului în mod text au acces la tot ecranul. Accesul poate fi limitat la o parte din ecran utilizând aşa numitele ferestre. Fereastra este un dreptunghi care este o parte a ecranului şi care poate fi gestionată independent de restul ecranului.

Un caracter de pe ecran, pe lângă coordonate, mai are şi următoarele atribute:

culoarea caracterului afişat;culoarea fondului;clipirea caracterului.

Aceste atribute sunt dependente de adaptorul grafic utilizat. Cele mai utilizate adaptoare sunt:

placa MDA, care este un adaptor monocrom;placa HERCULES, care este un adaptor color;placa CGA, care este un adaptor color;placa EGA, care este un adaptor color;placa VGA, care este un adaptor color de mare performanţă.

Pentru adaptoarele de mai sus se pot utiliza 8 culori de fond şi 16 culori pentru afişarea caracterelor.

Atributul unui caracter se defineşte cu ajutorul formulei:

atribut = 16 * culoare_fond + culoare_caracter + clipire (*)unde:culoare_fond (background) = cifră între 0 şi 7; (8 culori)culoare_caracter (foreground) = întreg între 0 şi 15; (16 culori)clipire = 128 (clipire) sau 0 (fără clipire)

Tabel cu numele culorilor:Culoare Constantă simbolică Valoare negru BLACK 0albastru BLUE 1verde GREEN 2turcoaz CYAN 3roşu RED 4purpuriu MAGENTA 5maro BROWN 6gri deschis LIGHTGRAY 7gri închis DARKGRAY 8albastru deschis LIGHTBLUE 9verde deschis LIGHTGREEN 10turcoaz deschis LIGHTCYAN 11roşu dechis LIGHTRED 12purpuriu magenta LIGHTMAGENTA 13galben YELLOW 14alb WHITE 15clipire BLINK 128

12.1. SETAREA ECRANULUI ÎN MOD TEXT- 79 -

Page 80: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Setarea (punerea) se realizează cu ajutorul funcţiei textmode care are prototipul:

void textmode (int modtext);unde: modtext poate fi exprimat numeric sau simbolic în felul următor:

Modul text activat Constantă simbolică Valoare alb/negru 40 coloane BW40 0color 40 coloane C40 1alb/negru 80 coloane BW80 2color 80 coloane C80 3monocrom MONO 7color 43 linii pentru EGAşi 50 linii pentru VGA C4350 64modul precedent LASTMODE -1

Modul MONO se poate seta pe un adaptor monocolor. Celelalte moduri se pot seta pe adaptoare color.

12.2. DEFINIREA UNEI FERESTREDacă dorim să partajăm ecranul în zone care să poată fi gestionate

independent trebuie să definim ferestre.O fereastră este o zonă dreptunghilară de pe ecran care se poate defini cu

funcţia window al cărei prototip este:void window (int stanga, int sus, int dreapta, int jos);

unde:-(stanga, sus) = coordonatele colţului din stânga al ferestrei;-(dreapta, jos) = coordonatele colţului din dreapta jos.La un moment dat o singură fereastră este activă şi anume aceea definită

de ultimul apel al funcţiei window. Funcţiile de gestionare a ecranului în mod text acţionează întotdeauna asupra ferestrei active. După setarea modului text cu ajutorul funcţiei textmode este activ tot ecranul.

Menţionăm că funcţia window nu are nici un efect dacă parametrii de la apel sunt eronaţi.

12.3. ŞTERGEREA UNEI FERESTREFereastra activă se poate şterge cu ajutorul funcţiei clrscr cu

prototipul:void clrscr(void);

fereastra activă (sau tot ecranul) devine activă; fondul are culoarea definită prin culoarea de fond curentă;

clrscr poziţionează cursorul în poziţia (1,1) a fiecărei ferestre.

12.4. GESTIUNEA CURSORULUIProgramatorul poate plasa cursorul pe un caracter al ferestrei folosind

funcţia gotoxy al cărei prototip este:void gotoxy (int coloana, int linie);

unde: (coloana, linie) sunt coordonate relative la fereastra activă, dacă sunt înafara ferestrei active apelul este ignorat;

Poziţia cursorului se poate determina cu ajutorul funcţiilor wherex şi wherey care au prototipurile:

int wherex (void); -returnează numărul coloanei în care se află cursorulint wherey (void); -returnează numărul liniei în care se află cursorul.

Există cazuri când se doreşte ascunderea cursorului. Acest lucru se poate realiza printr-o secvenţă specială în care se utilizează funcţia geninterrupt.

void ascundcursor (void) // face invizibil cursorul{ _AH = 1; _CH = 0x20; geninterrupt (0x10);}

Cursorul poate fi reafişat apelând funcţia de mai jos:- 80 -

Page 81: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

void afiscursor (void) // face vizibil cursorul{ _AH = 1; _CH = 6; _CL = 7; geinterrupt (0x10);}

AH, CH, CL reprezintă unii din regiştrii microprocesorului.

12.5. DETERMINAREA PARAMETRILOR ECRANULUIUtilizatorul are posibilitatea să obţină parametrii curenţi ai ecranului

prin apelarea funcţiei gettextinfo al cărui prototip este:

void gettextinfo (struct text_info *p);unde structura text_info este definită în fişierul conio.h astfel:

struct text_info{ unsigned char winleft; // amplasarea colturilor ferestrei unsigned char wintop;

unsigned char winright; unsigned char winbottom; unsigned char attribute;// culoarea fondului, a caracterelor si

unsigned char normattr; // clipirea unsigned char currmode;

unsigned char screenheight; // dimensiunea ecranului unsigned char screenwidth; unsigned char curx; // pozitia cursorului

unsigned char cury;};

12.6. MODURILE VIDEO ALB/NEGRUExistă două moduri (intens şi normal) care se pot activa cu funcţiile

highvideo şi lowvideo ale căror prototipuri sunt:

void highvideo (void);void lowvideo (void);Intensitatea iniţială este de obicei cea normală. Se poate reveni la

intensitatea normală dacă se apelează funcţia normvideo cu acelaşi prototip ca al celorlalte două funcţii.

Exemplu:Vom scrie un program care afişează parametrii ecranului:

#include <stdio.h>#include <conio.h>void main (void){ struct text_info par_ecran; gettextinfo (&par_ecran) ; printf (“\nstanga: %u sus: %u dreapta: %u jos: %u\n”,par_ecran.winleft,par_ecran.wintop,par_ecran.winright,par_ecran.winbottom); printf (“atribut: %d mod curent: %d\n”,par_ecran.normattr, par_ecran.currmode); printf (“inaltimea ecranului: %d latimea ecrnului: %d\n”,par_ecran.screenheight, par_ecran.screenwidth); printf (“coloana cursorului: %d linia cursorului: %d\n”,par_ecran.curx, par_ecran.cury);

}

12.7. SETAREA CULORILOR- 81 -

Page 82: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Culoarea fondului se setează cu ajutorul funcţiei textbackground cu prototipul următor:

void textbackground (int culoare);unde: culoare este un întreg cuprins între 0 şi 7 cu semnificaţia din

tabelul anterior al culorilor.

Culoarea caracterelor se setează cu ajutorul funcţiei textcolor cu prototipul următor:

void textattr (int culoare);unde: culoare este un întreg între 0 şi 15.

Se pot seta ambele culori, precum şi clipirea caracterului folosind funcţia textattr de prototip:

void textattr (int atribut);unde: atribut e definit cu ajutorul relaţiei (*) de la începutul lecţiei.

12.8. GESTIUNEA TEXTELORPentru afişarea caracterelor colorate în conformitate cu atributele

definite prin relaţia:atribut = 16 * culoare_fond + culoare_caracter + clipirese pot folosi funcţiile:putch - pentru afişarea unui caracter;cputs - pentru afişarea color a unui şir de caractere (acelaşi

prototip ca puts);cprintf - pentru afişarea color sub controlul formatelor.

Alte prototipuri de funcţii:void insline (void); - inserează o linie cu spaţii în fereastră,

liniile de sub poziţia cursorului se deplasează în jos cu o poziţie;void clreol (void) - şterge sfârşitul liniei începând cu poziţia

cursorului;void delline (void) - şterge toată linia pe care este poziţionat

cursorul;

int movetext ( int stanga, int sus, int dreapta, int jos, int stanga_dest, int dreapta_dest );

- copiază un text dintr-o poziţie în alta;returnează: 1 dacă textul s-a copiat cu succes şi o în caz de eroare.

Textele dintr-o zonă dreptunghiulară a ecranului pot fi salvate sau citite dintr-o zonă de memorie cu ajutorul funcţiilor puttext şi gettext şi au prototipurile:

int gettext (int stanga, int sus, int dreapta, int jos, void *destinatie);unde primii patru parametrii definesc fereastra unde se află textul de salvat;destinatie este pointerul spre zona de memorie în care se salvează textul.şiint puttext (int stanga, int sus, int dreapta, int jos, void *sursa);undeprimii patru parametrii definesc fereastra unde se va scrie pe ecran

textul preluat din memorie;sursa este pointerul spre zona de memorie din care se transferă textul.

Ele returnează:1 la copiere cu succes;0 la eroare.Observaţie:1o. Fiecare caracter de pe ecran se păstrează pe doi octeţi:pe un octet codul caracterului;pe octetul următor atributul caracterului.

Exemple:Programul următor setează o fereastră şi modurile video alb/negru.

- 82 -

Page 83: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

#include <conio.h>void main (void){ textmode (BW80); window (10,4,60,4); clrscr (); lowvideo (); cputs(”lowvideo”); normvideo (); cputs (“normvideo”); textmode (LASTMODE); cprintf (“\n\r Acionai o tast pentru a continua:”); getch ();}Programul următor afişează toate combinaţiile de culori posibile pentru

fond şi caractere (adaptor EGA/VGA).

#include <conio.h>#include <stdio.h>void main (void){ static char *tculoare [ ] = {“ 0 BLACK negru”, “ 1 BLUE albastru”, “ 2 GREEN verde”, “ 3 CYAN turcoaz”, “ 4 RED rosu”, “ 5 MAGENTA purpuriu”, “ 6 BROWN maro”, “ 7 LIGHTGRAY gri deschis”, “ 8 DARKGRAY gri inchis”, “ 9 LIGHTBLUE albastru deschis”, “10 LIGHTGREEN verde deschis”, “11 LIGHTCYAN turcoaz deschis”, “12 LIGHTRED rosu dechis”, “13 LIGHTMAGENTA purpuriu magenta”, “14 YELLOW galben “, “15 WHITE alb”}; int i,j,k; struct text_info atribut; gettextinfo (&atribut); for (i = 0; i < 8; i++ ) // i alege culoarea fondului

{ window (3,2,60,20); k=2; textbackground (i); clrscr(); for (j=0; j <10; j++, k++) // j alege culoarea caracterului

{ textcolor (j); gotoxy (2,k); if (i = = j) continue; cputs (tculoare[j]);}

gotoxy (1,18); printf (“actionati o tasta pentru contiuare\n”); getch();}

window (atribut.winleft, atribut.wintop, atribut.winright, atribut.winbottom);

textattr (atribut. attribute); clrscr();}

- 83 -

Page 84: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 13.- 84 -

Page 85: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

GESTIUNEA ECRANULUI ÎN MOD GRAFICModul grafic presupune că ecranul este format din “puncte luminoase”

(pixeli). Numărul acestora depinde de adaptorul grafic şi se numeşte rezoluţie. O rezoluţie este cu atât mai bună cu cât este mai mare. Adaptorul CGA are o rezoluţie de 200 rânduri x 640 de coloane iar EGA de 350 de rânduri x 640 de coloane. Adaptorul VGA şi SVGA oferă rezoluţii în funcţie de mărimea ecranului. Astfel adaptorul VGA şi SVGA oferă rezoluţii de până la 768 de rânduri x 1024 de coloane pentru ecranul de 14” (inches).

Pentru gestiunea ecranului în mod grafic se pot utiliza peste 60 de funcţii standard aflate în biblioteca sistemului. Aceste funcţii au prototiopurile în fişierul graphics.h. Totodată ele folosesc pointeri de tip far (de 32 de biţi).

13.1. SETAREA MODULUI GRAFICModul grafic se setează cu ajutorul funcţiei initgraph. Această funcţie

poate fi folosită singură sau împreună cu o altă funcţie numită detectgraph care determină parametrii adaptorului grafic. Prototipul ei este:

void far detectgraph (int far *graphdriver, int far *graphmode);unde:în zona spre care pointează graphdriver se păstrează una din valorile:

valoare simbol CGAMCGAEGAEGA64EGAMONOIBM8514HERCMONOATT400VGAPC3270

în zona spre care pointează graphmode se memorează una din valorile:pentru CGAvaloare simbol CGAC0CGAC1CGAC2CGAC3corespunzând toate pentru o rezoluţie de 320*200 de pixeli şi permit 4

culoriCGAHIare o rezoluţie de 640*200 puncte şi lucrează numai alb/negru.

pentru EGAvaloare simbol

EGALO are 640*200 i 16 culoriEGAHI are 640*350

pentru VGAvaloare simbol

0 VGALO are 640*2001 VGAMED are 640*3502 VGAHI are 640*480

Valorile spre care pointeză graphdriver definesc nişte funcţii standard corespunzătoare adaptorului grafic. Aceste funcţii se numesc drivere. Ele se află în subdirectorul BGI.

- 85 -

Page 86: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Funcţia detectgraph detectează adaptorul grafic prezent la calculator şi păstrează valoarea corespunzătoare acestuia în zona spre care pointează graphdriver. Modul grafic se defineşte în aşa fel încât el să fie cel mai performant pentru adaptorul grafic curent. Cele mai utilizate adaptoare sunt de tip EGA (calculatoare mai vechi) şi VGA şi SVGA (calculatoarele mai noi).

Apelul funcţiei detectgraph trebuie să fie urmat de apelul funcţiei initgraph. Aceasta setează modul grafic în conformitate cu parametrii stabiliţi de apelul prealabil al funcţiei detectgraph.

Funcţia initgraph are prototipul:

void far initgraph (int far * graphdriver, int far *graphmode, char far *cale);

unde:graphdriver şi graphmode sunt pointeri cu aceeaşi semnificaţie ca în cazul

funcţiei detectgraph;cale este un pointer spre şirul de caractere care definesc calea

subdirectorului BGI (Borland Graphic Interface), de exemplu:“C:\\BOLANDC\\BGI”Exemplu:Setarea în mod implicit a modului grafic:

int driver, mod_grafic;. . . detectgraph (&driver, &mod_grafic);initgraph(&driver, &mod_grafic, “C:\\BORLANDC\\BGI”);

După apelul funcţiei initgraph se pot utiliza celelalte funcţii standard de gestiune grafică a ecranului.

Din modul grafic se poate ieşi apelând funcţia closegraph care are prototipul:

void closegraph (void);

Funcţia initgraph mai poate fi apelată şi folosind constanta simbolică DETECT astfel:

int driver, mod_grafic;. . .driver = DETECT;initgraph (&driver, &mod_grafic, “C:\\BORLANDC\\BGI”);

Constanta simbolică DETECT este definită în fişierul graphics.h, alături de celelalte constante simbolice care definesc driverul. Aceasta are valoarea zero.

Prin apelul de mai sus, funcţia initgraph apelează funcţia detectgraph pentru a defini parametrii impliciţi ai adaptorului grafic.

Utilizatorul poate defini el însuşi parametri pentru iniţializarea modului grafic.

13.2. GESTIUNEA CULORILORAdaptoarele grafice sunt prevăzute cu o zonă de memorie în care se

păstrează date specifice gestiunii ecranului. Această zonă de memorie poartă denumirea de memorie video.

În mod grafic, ecranul se consideră format, după cum am precizat, din puncte luminoase numite pixeli. Poziţia pe ecran a unui pixel se defineşte printr-un sistem de coordonate (x,y) cu x coloana şi y linia în care este afişat pixelul.

În cazul adaptoarelor color, unui pixel îi corespunde o culoare. Culoarea pixelilor se păstrează pe biţi în memoria video. Memoria video necesară pentru a păstra starea ecranului setat în mod grafic, se numeşte pagină video. Adaptoarele pot conţine mai multe pagini video. Gestiunea culorilor este dependentă de tipul de adaptor grafic existent la microprocesor.

Numărul maxim de culori pentru adaptoarele EGA este de 64 (numerotate de la 0 la 63). Cele 64 de culori nu pot fi afişate simultan pe ecran. În cazul adaptorului EGA se pot afişa simultan 16 culori. Mulţimea culorilor care pot fi

- 86 -

Page 87: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

afişate simultan pe ecran se numeşte paletă. Culorile din componenţa unei palete pot fi modificate de utilizator prin intermediul funcţiilor standard. La iniţializarea modului grafic se setează o paletă implicită.

Pentru adaptorul EGA există un tablou de 64 de culori (cu coduri ntre 0 şi 63) din care se selectează cele 16 culori pentru paletă. Există şi constante simbolice foarte sugestive cu numele culorilor în engleză. Funcţiile de gestiune a culorilor pot avea ca parametri nu numai codurille culorilor ci şi aceste constante simbolice.

Culoarea fondului este întotdeauna cea corespunzătoare indicelui 0 din paletă. Culoarea pentru desenare este cea corespunzătoare indicelui 15.

Culoarea de fond poate fi modificată cu ajutorul funcţiei setbkcolor care are prototipul:

void far setbkcolor (int culoare);unde:culoare = index în tabloul care defineşte paleta.

Exemplu: setbkcolor (BLUE); setează culoarea de fond pe albastru.

Pentru a cunoaşte culoarea de fond curentă se poate apela funcţia getbkcolor de prototip:

int far getbkcolor (void);

Ea returnează indexul în tabloul care definete paleta pentru culoarea de fond.

Culoarea pentru desenare poate fi modificată folosind funcţia setcolor de prototip:

void far setcolor(int culoare);unde:culoare = index în tabloul care defineşte paleta.

Exemplu: setcolor (YELLOW); setează culoarea pentru desenare în galben.

Culoarea pentru desenare se poate determina apelând funcţia getcolor de prototip:

int far getcolor (void);

Ea returnează indexul în tabloul care defineşte paleta relativ la culoarea pentru desenare.

Paleta curentă poate fi modificată cu funcţiile setpalette şi setallpalette. Prima funcţie se foloseşte pentru a modifica o culoare din paleta curentă şi are prototipul:

void far setpalette (int index, int cod);

unde:index este un întreg din {0,. . . , 15} şi reprezintă indexul în tabloul

care defineşte paleta pentru culoarea care se modică;cod este un întreg din intervalul {0, 1,. . . , 63} şi reprezintă codul

culorii care o înlocuieşte în paletă pe cea veche.

Exemplu: setpalette (DARKGRAY, 45); modifică culoarea corespunzătoare

indicelui DARKGRAY (adică 8) prin culoarea de cod 45.

Funcţia setallpalette permite modificarea simultană a mai multor culori din compunerea paletei şi are prototipul:

void far setallpalette (struct palettetype far *paleta);

unde:palettetype este un tip definit în fişierul graphics.h astfelstruct palettetype

{ unsigned char size;- 87 -

Page 88: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

unsigned char colors [MAXCOLOR+1];};

cu - size dimensiunea paleteicolors tablou ale cărui elemente au ca valori codurile componente ale

paletei care se defineşte.

Modificarea paletei curente cu ajutorul funcţiilor setpalette şi setallpalette conduce la schimbarea corespunzătoare a culorilor afişate pe ecran în momentul apelului funcţiilor respective.

Pentru a determina codurile culorilor componente ale paletei curente se va folosi funcţia getpalette de prototip:

void far getpalette (struct palettetype far *paleta);

Paleta implicită poate fi determinată folosind funcţia getdefaultpalette de prototip:

struct palettetype *far getdefaultpalette(void);

Numărul culorilor dintr-o paletă poate fi obţinut apelând funcţia getmaxcolor de prototip:

int far getmaxcolor (void);

Funcţia returnează numărul maxim de culori diminuat cu 1. Deci în cazul adaptorului EGA funcţia returnează valoarea 15.

O altă funcţie care determină dimensiunea paletei este funcţia getpalettesize cu prototipul:

int far getpalettesize (void);

Funcţia returnează numărul culorilor componente ale paletei. În cazul adaptorului EGA funcţia returnează valoarea 16.

Exemplu:Programul următor afişează codurile culorilor pentru paleta implicită:

#include <graphics.h>#include <stdlib.h>#include <stdio.h>#include <conio.h>void main (void){ int gd = DETECT, gm, i; struct palettetype far *pal = (void *) 0; initgraph (&gd, &gm, “C:\\BORLANDC\\BGI”); pal = getdefaultpalette ();

for (i=0; i<16; i++){ printf (“colors[%d]=%d\n”, i, pal -> colors[i]); getch ();}

closegraph();}

13.3. STAREA ECRANULUIÎn mod grafic ecranul se compune din n*m pixeli. Pixelul din stânga-sus

are coordonatele (0,0), iar pixelul din dreapta-jos are coordonatele (n-1,m-1).În BGI există mai multe funcţii care permit utilizatorului să obţină

informaţii despre:coordonata maximă pe orizontală;coordonata maximă pe verticală;poziţia curentă (pixelul curent);etc.

int far getmaxx (void); returnează abscisa maximă;int far getmaxy (void); returnează ordonata maximă;

- 88 -

Page 89: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

int far getx (void); returnează poziţia pe orizontală (abscisa) pixelului curent;

int far gety (void); returnează poziţia pe verticală (ordonata) pixelului curent.

Exemplu:#include <graphics.h>#include <stdlib.h>#include <stdio.h>#include <conio.h>void main (void){ int gd = DETECT, gm, cul_fond, cul_desen, curent_x,curent_y, maxx, maxy; initgraph (&gd, &gm, “C:\\BORLANDC\\BGI”); cul_fond = getbkcolor(); cul_desen = getcolor(); maxx = getmaxx(); maxy = getmaxy(); curent_x = getx(); curent_y = gety(); closegraph(); printf (“culoarea fondului = %d\n”, cul_fond); printf (“culoare desenare = %d\n”, cul_desen); printf (“abscisa maxima = %d\n”, maxx); printf (“ordonata maxima = %d\n”, maxy); printf (“abscisa curenta = %d\n”, curent_x); printf (“ordonata curenta = %d\n”, curent_y); printf (“acionai o tasta pentru terminare”); getch();}

- 89 -

Page 90: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- 90 -

Page 91: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

LECŢIA 14.PROBLEME DIVERSE

De multe ori suntem puşi în faţa unor probleme pe care le înţelegem uşor dar nu ştim cum să le rezolvăm cât mai simplu şi elegant. Vă propunem câteva metode care bine însuşite pot duce, uneori, la o rapidă rezolvare a problemelor dificile. Evident că, nu toate problemele pot fi încadrate în aceste tipare propuse dar fiecare programator poate să-şi formeze un astfel de "portofoliu" de metode cu care să poate aborda orice problemă. Metodele prezentate în continuare pot constitui un început.

14.1. GENERAREA COMBINĂRILOR Fie o mulţime oarecare de n elemente care poate fi pusă într-o corespondenţă

biunivocă cu mulţimea A={1,...,n}. Ne propunem să determinăm toate m-combinările (m≤n) ale mulţimii A (submulţimi de m elemente ale mulţimii A). Vom rezolva problema, nerecursiv, pornind de la m-combinarea V=(1,2,...,m) determinând succesiv toate m-combinările ordonate lexicografic crescător.

Fie V=(v1,v2,...,vm) o m-combinare oarecare, atunci m-combinarea următoare în ordine lexicografică crescătoare se obţine astfel:

se determină cel mai mare indice i satisfăcând relaţiile:

vi<n-m+i, vi+1=n-m+i+1,..., vm-1=n-1, vm=n. (1)

se trece la vectorul următor:

(v1,...,vi-1,vi+1,vi+2,...,vi+n-i+1);

dacă nu există un astfel de indice i (care să satisfacă relaţiile (1)) înseamnă că vectorul V conţine ultima m-combinare şi anume: (n-m+1,n-m+2, ...,n).

Dăm în continuare o funcţie C care generează o m-combinare cu n elemente având ca parametru cod o variabilă booleană care pentru valoarea 0 generează prima m-combinare iar pentru valoarea 1 generează următoarea m-combinare. Funcţia comb reîntoarce valoarea 1 dacă s-a generat o m-combinare oarecare şi valoarea 0 dacă s-a generat ultima m-combinare (în acest caz cod are valoarea 0). Se va folosi un vector global v în care se generează m-combinările.

#define dim 50#include <stdio.h>int v[dim+1], n, m;// functia generatoare a m-combinarilorint comb(cod)int cod;{ int i,j;

// generarea primei m-combinari 1,...,m if (cod == 0) { for (i=1; i<=m; v[i]=i++); return (1); } i=m+1;

// cautarea indicelui i pentru satisfacerea relaiilor (1)

do { i-- } while (v[i] >= n-m+i); if (i) {

v[i]=v[i]+1; for (j=i+1; j<=m; v[j]=v[j-1]+1,j++); return (1);}

else return (0);- 91 -

Page 92: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

}

void main(void){ int kod,i; printf("\ndati n: "); scanf ("%d",&n); printf("\ndati m: "); scanf ("%d",&m); comb(0); kod=1; while (kod) { printf("\n"); for (i=1;i<=m;printf ("%3d",v[i++])); kod = comb(kod); } getche();}

14.2. METODA GREEDY Se aplică problemelor în care se dă o mulţime A conţinând n date (de orice

natură şi structură) de intrare cerându-se să se determine o submulţime B (B⊆A) care să îndeplinească anumite condiţii pentru a fi acceptată. Cum, în general, există mai multe astfel de submulţimi (numite soluţii posibile) se mai dă şi un criteriu conform căruia dintre aceste submulţimi să alegem una singură (numită soluţie optimă) ca rezultat final. Foarte multe probleme de căutare se înscriu în acest tip.

Menţionăm că, în general, soluţiile posibile au următoarele proprietăţi: - se presupune că ∅ este soluţie posibilă; - dacă B este soluţie posibilăţi C⊆B atunci şi C este soluţie posibilă;Vom da în continuare o variantă a tehnicii greedy (denumire care în

traducere înseamnnă lăcomie, înghiţire) în care se porneşte de la mulţimea vidă. Apoi se alege pe rând, într-un anumit fel, un element din A neales încă. Dacă adăugarea lui la soluţia parţial anterior construită conduce la o soluţie posibilă, atunci se adaugă, altfel se alege un nou element. Tot acest procedeu se repetă pentru toate elementele din A. Dăm în continuare în pseudocod o procedură:

procedure GREEDY (n,A,B) B:=∅; for i=1,n do call ALEGE (A,i,x); call POSIBIL (B,x,cod); if cod=1 then call ADAUG (B,x); endif; endfor; return; end procedure

Despre procedurile apelate din GREEDY precizăm următoarele:procedura ALEGE furnizează în x un element din A aj∈{ai,...,an} şi

interschimbă ai cu aj; dacă la fiecare pas se cercetează ai atunci procedura se simplifică;

procedura POSIBIL verifică dacă elementul x poate fi adăugat sau nu mulţimii parţiale construită până la pasul curent furnizând o valoare booleană cod cu semnificaţia:

cod = 1, dacă B U {x}, este soluţie posibilăcod = 0, altfel

procedura ADAUG înlocuieşte pe B cu B∪{x}.

Obs. Procedurile de mai sus nu sunt necesare întotdeauna, acest fapt depinzând de complexitatea problemei. Oricum trebuie reţinut cadrul de rezolvare al acestui tip de problemă.

- 92 -

Page 93: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Problemă rezolvată

Se dă o mulţime de valori reale X={x1, . . .,xn}. Se cere submulţimea Y⊂X astfel ca ∑ y /y∈Y, să fie maximă.

Evident că problema este foarte simplă (în Y trebuie introduse elementele strict pozitive din X; evităm să mai construim procedurile ALEGE, POSIBIL, ADAUG) şi vom da rezolvarea ei în pseudocod:

procedure SUMA (n,X,Y,k) integer n,X(n),Y(n),k k:=0; for i=1,n do if x(i) > 0 then k:=k+1; y(k):=x(i) endif; endfor; return; end procedureProbleme propuse

14.2.1.Se dau n şiruri S1,S2,...,Sn ordonate crescător, de lungimi L1,L2, ...,Ln. Să

se obţină un şir S de lungime L1+L2+...+Ln cu toate elementele celor n şiruri ordonate crescător (problemă de interclasare).

Indicaţie: Vom interclasa succesiv câte două şiruri în final obţinând şirul ordonat crescător. Complexitatea interclasării a două şiruri A şi B de lungimi a şi b depinde direct proporţional de a+b (pentru că se fac a+b deplasări de elemente). Se pune problema găsirii ordinii optime în care trebuie efectuate aceste interclasări astfel ca numărul total de deplasări să fie minim.

Vom da un exemplu pentru a lămuri mai bine lucrurile:fie 3 şiruri de lungimi (90,40,10). Interclasăm şirul S1 cu S2 apoi şirul

rezultat cu S3; putem nota acest lucru formal prin (S1+S2)+S3. Se obţin (90+40) + (130+10)=270 deplasări. Dacă vom interclasa şirurile în ordinea (S2+S3)+S1 vom obţine (40+10)+ (50+90)=190 de deplasări. De aici concluzia că întotdeauna vom interclasa şirurile de lungimi minime din şirurile rămase.

14.2.2.Găsiţi tripletele de numere pitagorice din Nn x Nn x Nn (prin Nn notat

mulţimea {0,1,2,...,n}). Încercaţi optimizarea timpului de execuţie a programului.

14.2.3. Fie mulţimea m-combinărilor luate din n elemente şi fie k<m un număr

natural. Să se dea un algoritm şi apoi să se scrie un program C astfel încât să se determine o submulţime de m-combinări cu proprietatea că oricare două m-combinări au cel mult k elemente comune.

14.2.4.Să se determine mulţimile interior stabile (MIS) ale unui graf oarecare dat

prin matricea sa de adiacenţă.

14.3. METODA BACKTRACKING (CĂUTARE CU REVENIRE) Această metodă se aplică problemelor ce pot fi reprezentate sub forma unui

arbore finit iar căutarea soluţiei presupune parcurgerea arborelui în adâncime (DF=Depth First).

Problemele de acest tip au în general soluţia de forma x=(x1, . . . ,xn) ∈ S1x . . . xSn, fiecare Sk fiind o mulţime finită. Mai facem câteva precizări preliminare:

a) pentru fiecare problemă sunt date anumite relaţii între componentele x1, . . . ,xn ale lui

x numite condiţii interne;- 93 -

Page 94: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

b) produsul cartezian S=S1x...xSn se mai numeşte spaţiul soluţiilor posibile, iar soluţiile

posibile care satisfac condiţiile interne se numesc soluţii rezultat;c) în general se cer două lucruri în astfel de probleme: - determinarea tuturor soluţiilor rezultat; - determinarea doar a acelor soluţii care optimizează o funcţie obiectiv

dată.Cum rezolvăm astfel de probleme? Există două modalităţi de rezolvare:1) tehnica directă (numită şi tehnica forţei brute) prin care se generează

toate elementele spaţiului de soluţii posibile şi apoi se verifică fiecare dacă este sau nu o soluţie rezultat; această tehnică necesită un timp prohibitiv (dacă fiecare Si are doar 2 componente complexitatea este O(2n); totodată ar fi necesară imbricarea a n cicluri cu n aprioric necunoscut).

2) backtracking care evită generarea tuturor soluţiilor posibile.Să dăm în continuare câteva repere ale rezolvării:

soluţia este construită progresiv, componentă cu componentă;lui xk i se atribuie o valoare (evident că numai din Sk) dacă şi numai dacă

x1, . . . ,xk-1 au deja valori;se verifică condiţiile de continuare (strâns legate de condiţiile interne)

dacă are sens trecerea la xk+1;dacă nu are sens (adică condiţiile de continuare nu sunt îndeplinite atunci

facem o nouă alegere pentru xk sau dacă am epuizat valorile din Sk atunci k:=k-1 i se face o nouă alegere pentru xk-1; ş.a.m.d.

2. dacă are sens atunci k:=k+1 şi se testează dacă k>n: 21) dacă inegalitatea este adevărată atunci se afişează soluţia

astfel determinată şi k:=k-1 continuând procesul de la punctul 1;

22) dacă inegalitatea este falsă se continuă procesul de la punctul 1.

Procedura rezolvării unor astfel de probleme este:

procedure BT(n,x) integer n; array x(n); k:=1; while k>0 do cod:=0; while ([mai exist o valoare α din Sk netestat] and cod=0)

x(k):=α; if ∅k(x(1),...,x(k)) then cod:=1; endif; endwhile; if cod=0 then k:=k-1 else if k=n then write (x(1),...,x(n)) else k:=k+1 endif endif; endwhile; return; end procedureVom face câteva precizări:1o. Predicatul ∅k(x1, . . . , xk) reprezintă condiţiile de continuare pentru

x1, . . . , xk;2o. Cod este o valoare ce indică îndeplinirea/neîndeplinirea condiţiilor de

continuare;3o. Dacă predicatul ∅k(x1, . . . , xk) este adevărat ∀ k ∈ {1,...,n} atunci

se vor afla toţi vectorii din S;4o. Backtracking poate fi uşor reprezentat pe un arbore construit astfel:

- nivelul 1 conţine rădăcina;- din orice nod de pe nivelul k pleacă sk muchii spre nivelul k+1,

etichetate cu cele sk elemente ale lui Sk;- nivelul n+1 va conţine s1*s2*. . .* sn noduri frunză;

- 94 -

Page 95: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- pentru fiecare nod de pe nivelul n+1 etichetele muchiilor conţinute în drumul ce

leagă rădăcina de acest nod reprezintă o soluţie posibilă;

Dacă mulţimile Sk reprezintă progresii aritmetice atunci algoritmul general se modifică astfel:

procedure BTA(n,a,b,h) integer n; array primul(n),ultimul(n),ratia(n),x(n); k:=1; x(1):=primul(1)-ratia(1); while k>0 do cod:=0; while ( x(k)+ratia(k) ≤ ultimul(k) and cod=0 )

x(k):=x(k)+ratia(k); if ∅k(x(1),...,x(k)) then cod:=1 endif; endwhile; if cod=0 then k:=k-1 else if k=n then write (x(1),...,x(n)) else k:=k+1

x(k):=a(k)-h(k) endif endif; endwhile; return; end procedure

unde:- primul(n) reprezintă vectorul primilor termeni ai progresiilor

aritmetice;- ultimul(n) reprezintă vectorul ultimilor termeni ai progresiilor

aritmetice;- ratia(n) reprezintă vectorul raţiilor progresiilor aritmetice;

De reţinut cele două avantaje ale acestui procedeu:evitarea imbricării unui număr oarecare de cicluri aprioric variabil (în

algoritmul propus se imbrică doar două cicluri pretestate while);evitarea construirii spaţiului tuturor soluţiilor posibile S1xS2x . . . xSn.

Problemă rezolvată

În câte moduri se pot aranja 8 dame pe tabla de şah astfel încât să nu se "bată" reciproc. Să se folosească al doilea algoritm dintre cei menţionaţi anterior.

Prima variantă

Acest program respectă algoritmul anterior cu unele mici modificări. Facem precizarea că vectorul x conţine în componenta x[i] numărul coloanei de pe tabla de şah pe care se va afla dama de pe linia i. Tipărirea va reprezenta o permutare (din cele 8! soluţii posibile). Se vor afla 92 de soluţii. Lăsăm pe seama cititorului să deducă analogiile şi diferenţele între algoritm şi program.

#include <stdio.h>#include <math.h>

void main (void){ int x[9],cod,k,i,nr; k=1; x[1]=0;nr=0; while (k>0) { cod=0;

- 95 -

Page 96: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

while (((x[k]+1)<=8)&&(cod= =0)) { x[k]++; if ((k= =1) && (x[k]= =1)) cod=1; else { i=1; cod=1;

while ((cod==1)&&(i<k)) { if ((x[i]==x[k])||(abs(x[i]-x[k])==k-i)) cod=0; i++; } }

} if (cod==0) k--; else {if (k==8)

{ printf("\n%3d. ",++nr); for (i=1;i<9;printf("%d ",x[i++])); }}

else x[++k]=0; }

}}

A doua variantă:

#include <stdio.h>#include <math.h>#define n 100int x[100],cod,k,nc,nsol;

int Verifica(void){ int i,cod1; // cod1=1 conditiile de continuare cod1=1; // sunt verificate if (k>1) // cod1=0 in caz contrar; for(i=1; i<= (k-1); i++) { if (x[k]= =x[i]) cod1=0; if (abs(x[k]-x[i]) = = k-i) cod1=0; // (*) } return cod1;}

void ScrieSolutie(void){ int i; printf("\n%3d. ",++nsol); for (i=1; i<=nc; printf("%3d",x[i++]));}

void CitesteDate(void){ int i; nsol=0; clrscr(); nc=n+1; while ((nc>n) || (nc<0)) { printf ("Dati n = "); scanf ("%d",&nc); };}void main (void){ CitesteDate(); k=1; x[1]=0; while (k>0) { cod=0; while (((x[k]+1) <= nc) && (cod= =0)) {x[k]++; cod=Verifica(); } if (cod= =0) k--;

else {if (k==nc) ScrieSolutie();- 96 -

Page 97: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

else x[++k]=0; }

}}

A doua variantă este modulară, mai uşor de înţeles şi generalizează problema damelor până la tabla de şah de 100x100. Lăsăm pe seama cititorului modificarea funcţiei ScrieSolutie pentru a afla în mod grafic tabla de şah.

Dacă în funcţia Verifica se şterge instrucţiunea notat cu (*) atunci se obţin toate permutările de n obiecte.

Probleme propuse 14.3.1Să se rezolve problema turelor de şah după al doilea algoritm. În câte

moduri se pot aranja n turnuri pe tabla de şah astfel încât să nu se "bată" reciproc.

14.3.2.Să se afişeze poziţiile succesive ale unui cal pe tabla de şah, pornind

dintr-o poziţie dată, astfel încât să fie atinse toate căsuţele tablei de şah.

14.3.3.Având un fişier cu o mulţime de cuvinte din limba română de aceeaşi lungime

k să se scrie un program C care afişează toate careurile rebusiste fără puncte negre. ( Problema e fascinantă implicând şi cunoştinţe gramaticale dar şi cunoscând faptul că nu s-au construit careuri de 10x10 fără puncte negre manual şi nici cu ajutorul calculatorului; se poate încerca apoi şi cu k:=11,12, . . .).

14.3.4.Un intreprinzător dispune de un capital C şi are n variante de investiţii.

Pentru fiecare investiţie i cunoaşte fondurile de investiţie fi precum şi beneficiile bi. Se cere un program care să deducă toate variantele posibile de investiţii al intreprinzătorului. Se mai dau şi condiţiile C≥ci ∀ i∈ {1, . . . ,n}.

14.3.5.Având un graf neorientat caracterizat prin matricea costurilor să se

determine prin bactraking circuitul de cost minim pornind dintr-un vârf dat.

14.3.6.Având un graf neorientat caracterizat prin matricea de incidenţă a

vârfurilor să se determine prin bactraking mulţimile interior stabile maximale.

14.3.7.Să se determine toate cuvintele binare de lungime 10 care conţin exact 5

cifre de 1.

14.3.8.Să se determine toate cuvintele binare de lungime 10 care conţin cel mult 5

cifre de 1.

14.3.9.Să se determine toate cuvintele din {a,b,c}* (mulţimea tuturor cuvintelor

peste alfabetul Σ se notează cu Σ* ) de lungime 10 care conţin exact 2 simboluri 'a'; 3 simboluri 'b' şi 5 simboluri 'c'.

14.3.10.Să se determine toate cuvintele din {a,b,c}* de lungime n care conţin exact

na simboluri 'a'; nb simboluri 'b' şi nc simboluri 'c' (cu condiţia n=na+nb+nc).

14.3.11.Să se determine toate tripletele (x1,x2,x3) de numere astfel ca: x1+x2+x3≤suma x1*x2*x3≤produs

- 97 -

Page 98: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

cu valorile suma şi produs date iar x1∈S1, x2∈S2 şi x3∈S3 ; S1, S2 şi S3 fiind trei progresii aritmetice date deasemenea.

14.3.12.Să se determine toate variantele de pronosport cu 13 rezultate din {1,x,2}

care conţin exact n1 simboluri '1'; nx simboluri 'x' şi n2 simboluri '2' (cu condiţia n1+nx+n2=13).

14.3.13.Să se determine toate variantele de pronosport cu 13 rezultate din {1,x,2}

care conţin cel mult n1 simboluri '1'; cel mult nx simboluri 'x' şi simboluri '2' în rest (cu condiţia n1+nx≤13).

14.4. METODA DIVIDE ET IMPERA (DIVIDE ŞI STĂPÂNEŞTE) Această modalitate de elaborare a programelor constă în împărţirea repetată

a unei probleme de dimensiune mai mare în două sau mai multe subprobleme de acelaşi tip urmată de combinarea soluţiilor subproblemelor rezolvate pentru a obţine soluţia problemei iniţiale.

Se dă un vector A=(a1,...,an) şi trebuie efectuată o prelucrare oarecare asupra elementelor sale.

Presupunem că:∀ p,q∈{1,...,n} cu 1 ≤p < q ≤ ∃ m∈{p,p+1,...,q-1} a.î. prelucrarea

secvenţei {ap,...,aq} se poate face prelucrând subsecvenţele:{ap,...,am} şi {am+1,...,aq} şi apoi combinând rezultatele pentru a obţine

prelucrarea întregii secvenţe {ap,...,aq}.Dacă se reuşeşte o astfel de formalizare a problemei atunci ea poate fi

rezolvată cu ajutorul acestei metode.Vom da în continuare o procedură recursivă în pseudocod:

procedure DI (p,q,α) if q-p ≤ eps then call PREL (p,q,α) else call IMPARTE (p,q,m) ; call DI (p,m,β); call DI (m+1,q,γ); call COMB (β,γ,α); endif; return; end procedure

Câteva precizări se impun:procedura trebuie apelată prin call DI (1,n,α) în α obţinându-se rezultatul

final;eps este lungimea maxim a unei secvene {ap,...,aq} notată prin (p,q) pentru

care se face prelucrarea directă fără a mai fi necesară împărţirea în subprobleme;procedura PREL realizează prelucrarea directă a secvenţelor (p,q);procedura COMB realizează combinarea rezultatelor β şi γ ale prelucrării a

două secvenţe vecine (p,m) şi (m+1,q) obţinând rezultatul α al prelucrării întregii secvenţe (p,q);

prin procedura IMPARTE se obţine valoarea lui m.Vom da ca exemplu problema sortării crescătoare a unui şir de întregi

prin interclasare. deoarece secvenţele (i,i+1) sunt uşor de ordonat acestea vor constitui

secvenţele ce se vor prelucra, deci eps = 1;- m se va calcula ca (p+q)/2, deci nu mai e nevoie de procedura specială

IMPARTE;procedura COMB va interclasa întotdeauna două secvenţe (p,m) şi (m+1,q)

ordonate crescător;vom folosi un vector x drept structură globală şi vom face toate

prelucrările pe elementele sale nemaiavând nevoie de zonele α,β;pentru zona γ vom folosi un vector local y în procedura COMB acesta

conţinând elementele corespondente din x dar ordonate crescător; tot în procedura COMB se vor copia apoi elementele lui y din porţiunea (p,q) în x;

- 98 -

Page 99: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

evident că procedurile din schema generală a algoritmului sunt funcţii C cititorul făcând analogiile necesare.

#include <stdio.h>#include <conio.h>#define nrelem 100int x[nrelem];int n;

void PREL (int p, int q){int aux; if ((p<q) && (x[p] > x[q])) {

aux=x[p]; x[p]=x[q]; x[q]=aux; }

}

void COMB (int inf, int mijloc, int sup){int i,j,k,l; int y[nrelem]; i=k=inf; j=mijloc+1; while (i<=mijloc && j<=sup) { if (x[i] <= x[j])

y[k++]=x[i++]; else

y[k++]=x[j++]; } for(l=i; l<=mijloc; y[k++]=x[l++]); for(l=j; l<=sup; y[k++]=x[l++]); for(k=inf; k<=sup; x[k++]=y[k]);}

void DI (int p, int q){ int m; if ((q-p) <= 1)

PREL (p,q); else

{m=(p+q)/2; DI (p,m); DI (m+1,q); COMB (p,m,q);}

return;}

void main(void){int i; clrscr(); printf ("dati nr de elemente\n"); scanf ("%d",&n); for (i=1; i<=n; i++) {printf("x[%d]=",i); scanf("%d",&x[i]); } DI (1,n); printf("\nsirul sortat crescator este:\n"); for (i=1; i<=n; i++) printf("x[%d]=%d\n",i,x[i]); getch();}

- 99 -

Page 100: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

14.5. METODA PROGRAMĂRII DINAMICE Această metodă este aplicabilă problemelor de optim în care soluţia poate fi

privită ca rezultatul unui şir de decizii d1, . . . ,dn. Fiecare decizie depinde de deciziile deja luate şi nu este unic determinată (spre deosebire de tehnica greedy unde fiecare decizie care se ia trebuie să fie unică). Totodată este necesar să fie satisfăcută una din variantele principiului optimalităţii pentru a putea fi aplicată această metodă.

Vom formaliza acest principiu al optimalităţii:fie d1,...,dn un şir optim de decizii (SOD) care transformă starea so în

starea sn, trecând prin stările intermediare s1, . . . ,sn-1; vom nota acest fapt prin (d1,dn) este SOD pentru perechea de stări (so,sn);

grafic procesul este descris ca în figura următoare:

d1 d2 dn*----->*---->*------>* . . . *------>*so s1 s2 sn-1 sn

Vom da acum mai multe variante ale principiului optimalităţii:

dacă (d1,dn) este SOD pentru (so,sn) atunci (d2,dn) este SOD pentru (s1,sn);

2) dacă (d1,dn) este SOD pentru (so,sn) atunci ∀ i din {1, . . . ,n-1} avem a) (d1,di) este SOD pentru (so,si) SAU b) (di+1,dn) este SOD pentru (si,sn).

3) dacă (d1,dn) este SOD pentru (so,sn) atunci ∀ i din {1, . . . ,n-1} avem a) (d1,di) este SOD pentru (so,si) ŞI b) (di+1,dn) este SOD pentru (si,sn).

Ultima variantă este cea mai generală şi mai completă. Odată verificat o formă a principiului optimalităţii, metoda programării dinamice constă în a scrie relaţiile de recurenţă şi apoi de a le rezolva. În general relaţiile de recurenţă sunt de 2 tipuri :

fiecare decizie di depinde de di+1,...,dn - relaţii de recurenţă înainte, deciziile vor fi luate în ordinea dn, dn-1, . . . ,d1;

fiecare decizie di depinde de d1, . . . ,di-1 - relaţii de recurenţă înapoi, deciziile vor fi luate în ordinea d1, d2, . . . , dn.

Problemă rezolvată

Fie G=(X,Γ) un 1-graf orientat căruia îi ataşăm matricea costurilor, (fiecare arc (i,j)∈Γ este etichetat cu o valoare reală strict pozitivă). Se pune problema găsirii drumului de valoare minim (DVM) între oricare 2 vârfuri i şi j.

Rezolvare

Verificăm prima variantă a principiului optimalităţii:- fie i, i1, i2, . . . , im, j DVM între i şi j atunci evident că i1, . . . ,

im, j este DVM între i1 şi j;- notăm prin L(i,k) lungimea DVM dintre i şi k, ∀ k∈X;- notăm deasemenea dkj valoarea arcului (k,j);- ecuaţiile de recurenţă sunt:

L(i,j) = min {L(i,k) + dkj)} (k,j)∈Γ

#include<stdio.h>#include<values.h>#define nn 10int d[nn+1][nn+1],i,j,n;

int L(int i,int j)

- 100 -

Page 101: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

{ int m=MAXINT; int k,x=0; if (i= =j) m=0; else

for (k=1;k<=n;k++) if (d[k][j] < MAXINT)

{ x=L(i,k)+d[k][j]; if (x<m) m=x;}

return m; }

void citestematrice(void){int i,j; printf("\ndati dimensiunea matricii distantelor : "); scanf("%d",&n);

for(i=1;i<=n;i++) for(j=1;j<=n;j++) { printf("d[%d,%d]= ",i,j); scanf ("%d",&d[i][j]); } for(i=1;i<=n;i++) for(j=1;j<=n;j++) if (d[i][j]= = -1) d[i][j]=MAXINT;}

void citeste(void){ printf("\ndati varful initial : "); scanf("%d",&i); printf("\ndati varful final : "); scanf("%d",&j);}

void afiseaza(int val){ printf("\nvdlm intre varful %d si %d este %d",i,j,val);}

void main(void){ int vdlm; clrscr(); citestematrice(); citeste(); vdlm=L(i,j); afiseaza(vdlm); getch();}

Probleme propuse

14.5.1.Se dă o matrice A=(aij), i=1,...,n; j=1,...,m cu elementele din mulţimea

{0,1,2}. Două elemente din A aij şi akl se numesc 4-vecine dacă i-k+j-l = 1.Notăm cu So, S1 şi S2 submulţimile formate din elementele matricii egale cu

0, 1 respectiv 2. Submulţimea S1 se împarte în grupuri astfel: aij şi akl fac parte din acelaşi grup dacă sunt 4-vecine sau dacă ∃ apq ∈ S1 : aij şi apq sunt 4-vecine iar apk şi akl sunt din acelasi grup. Un element akl ∈ So îl vom numi spion al grupului G ∈ S1 dac ∃ aij ∈ G a.î. akl şi aij s fie vecine. Un spion este perfect dac are toţi vecinii din G.

Se cere:a) cel mai numeros grup (⊆S1) care are un singur spion (dacă există).b) toate grupurile care au cel puţin doi spioni perfecţi.

14.5.2.

- 101 -

Page 102: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

Se dă o matrice cu elemente care au valori din {d,o}, care reprezintă un teren cu drumuri {d} şi obstacole {o}. În acest teren se află un tanc şi o ţintă. Acest tanc poate primi comenzi pentru rotirea ţevii (cu 90o în ambele sensuri), pentru deplasare (în direcţia ţevii cu o linie sau cu o coloană) sau pentru tragere (în direcţia ţevii pentru a nimeri ţinta) în cazul în care între tanc şi ţintă nu există nici un obstacol. Considerând că deplasarea nu se poate efectua prin obstacole, se cere cel mai scurt drum necesar tancului pentru a putea distruge ţinta şi şirul comenzilor efectuate în cazul în care exist un astfel de drum.

14.5.3.Se d o matrice cu elemente din {0,1,2,3}, reprezentând o pădure cu capcane

(0) şi cărări (1). În această pădure se află o vulpe (2) şi mai mulţi lupi (3). Fiecare lup încearcă să se apropie de vulpe fără a şti unde se află capcanele, iar în cazul în care cade într-o capcană, moare. Vulpea încearcă să se îndepărteze de cel mai apropiat lup, având însă posibilitatea să descopere şi capcanele şi să le ocolească. Atât vulpea cât şi lupii îi pot modifica poziţia doar cu o linie sau cu o coloană în fiecare moment. Să se spună dacă vulpea reuşeşte să scape de lupi.

14.5.4.Se consideră un teren dreptunghiular sub forma unei matrici A de m linii şi

n coloane. Elementele aij ale matricii conţin cotele (înălţimile) diferitelor porţiuni astfel obţinute. Se presupune că o bilă se găseşte pe o porţiune (io,jo) având cota a(io,jo).

Se cere un program care să precizeze toate traseele (începând cu (io,jo) ) posibile pe care le poate urma bila spre a ieşi din teren, ştiind că bila se poate deplasa în orice porţiune de teren 4-învecinat cu o cotă strict inferioară cotei terenului pe care se găseşte bila.

14.5.5.Se cere un program care rezolvă problema labirintului (nerecursiv).O matrice de m linii şi n coloane reprezintă un labirint dacă:∃ a(i,j) = o - semnificând culoar;∃ a(i,j) = 1 - semnificând zid.Un traseu de ieşire pornind de la o porţiune (io,jo) trebuie să fie o

succesiune de perechi (io, jo), (i1, j1) . . . (ik, jk) astfel încât 2 perechi învecinate să fie 4-vecine şi a(ip,jp)=0

∀ p=0, . . . ,k.

- 102 -

Page 103: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

ANEXĂUN MEMENTO AL SINTAXEI LIMBAJULUI C

SINTAXA LIMBAJULUI CÎn descrierea sintaxei limbajului vom folosi următoarele notaţii: a) ::= cu semnificaţia "prin definiţie este"; b) pentru a separa alternativele unei definiţii sintactice; c) < > pentru definirea unor metavariabile; d) [ ] pentru delimitarea elementelor opţionale; e) .. pentru a indica elementele care se pot repeta.

Exemplu: Pentru a defini un "întreg_zecimal" ca o succesiune de cifre semnificative vom scrie:

<Întreg_zecimal> ::= <Cifră_nenulă> [<Cifră>] ..

A. Atomi lexicali

<Cuvinte_cheie> ::= autobreakcasecharconstcontinuedefaultdodouble|elseenumexternfloatforgotoifint|longregister |returnshortsignedsizeofstatic|struct switchtypedefunionunsignedvoidvolatilewhile

<Nume> ::= <Literă>[<Literă><Cifră>] ..

<Literă> ::= abcdefghijklmnopqrstuvwxyzBCDEFGHIJKLMNOPQRS TUVWXYZ_

<Cifră> ::= 0123456789

<Constantă> ::= <Constantă_întreagă><Constantă_reală> <Constantă_enumerare><Constantă_caracter>

<Constantă_întreagă> ::= <Număr_întreg>[<Sufix_întreg>

<Număr_întreg> ::= <Întreg_zecimal><Întreg_hexazecimal><Întreg_octal>

<Întreg_zecimal> ::= <Cifră_nenulă>[<Cifră>] ..

<Cifră_nenulă> ::= 123456789

<Întreg_hexazecimal> ::= 0x<Cifră_hexa> ..

<Cifră_hexa> ::= <Cifră>abcdefABCDEF

<Întreg_octal> ::= 0[<Cifră_octală>] ..

<Cifră_octal_> ::= 01234567

<Sufix_întreg> ::= <Sufix_unsigned>[<Sufix_long>]<Sufix_long>[<Sufix_unsigned>]

<Sufix_unsigned> ::= Uu

<Sufix_long> ::= Ll

<Constantă_reală> ::= <Constantă_fract>[<Exponent>][<Sufix_real>] <Cifră> .. <Exponent>[<Sufix_real>]

<Constantă_fract> ::= [<Cifră>] .. . <Cifră>..<Cifră>.. .

<Exponent> ::= Ee[+-]<Cifră> ..

- 103 -

Page 104: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

<Sufix_real> ::= FfLl

<Constantă_enumerare> ::= <Nume>

<Constantă_caracter> ::= '<Caracter_C> ..'L'<Caracter_C>'

<Caracter_C> ::= <Orice_caracter_tipăribil_cu excepţia: ' i \> <Secvenţă_escape>

<Secvenţă_escape> ::= \"\'\?\\\a\b\f\n\r\t\v \<Cifră_octală>[<Cifră_octală>[<Cifră_octală>]] \x<Cifră_hexa> ..

<Şir_de_caractere> ::= "[<Caracter_S> ..]"

<Caracter_S> ::= <Orice_caracter_tipăribil_cu excepţia: ' şi \><Secvenţa_escape>

<Operatori_şi_semne_de_punctuaţie >::= +-*/%^&~!=->+=-=*= /=%=^=&=!=<<>><<=>>== =<= >=<>&&!!++--,()[]{};?:...

B. Declaraţii

<Unitate_de_compilare> ::= <O_declaraţie> ..

<O_declaraţie> ::= <Def_funcţie><Declaraţie>;

<Declaraţie> ::= [<Specificatori>][<Listă_declaratori>]

<Specificatori> ::= <Specificator> ..

<Specificator> ::= <Clasă_memorare><Tip>typedef

<Clasă_memorare> ::= autoregisterstaticextern

<Tip> ::= <Tip_simplu><Nume_typedef><Calificator> <Descriere_tip_enumerare><Descriere_tip_neomogen>

<Tip_simplu> ::= charshortintlongsignedunsignedfloatdouble void

<Nume_typedef> ::= <Nume>

<Descriere_tip_enumerare> ::= enum<Nume>enum[<Nume>]{<Lista_enum>}

<Lista_enum> ::= <Enumerator>[,<Enumerator>] ..

<Enumerator> ::= <Nume>[=<Expr_constant_>{]

<Descriere_tip_neomogen> ::= <Tip_neomogen><Nume> <Tip_neomogen>[<Nume>]{<List__câmpuri>}

<Tip_neomogen> ::= structunion

<Listă_câmpuri> ::= <Decl_câmpuri>;[<Decl_câmpuri>;] ..

<Decl_câmpuri> ::= [<Specificatori>][<Câmp>[,<Câmp>] ..]

<Câmp> ::= <Declarator>[<Nume>]:<Expr_constantă>

<Listă_declaratori> ::= <Decl_init>[,<Decl_init>] ..

- 104 -

Page 105: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

<Decl_init> ::= <Declarator>[<Iniializare>]

<Declarator> ::= [<Indirect>..]<Denumire>[<Decl_F_T>]

<Indirect> ::= *[<Calificator> ..]

<Denumire> ::= <Nume>(<Declarator>)

<Decl_F_T> ::= ([<L_declar_par>])<Decl_tablou> ..

<Decl_tablou> ::= [][<Expr_constantă>]

<L_declar_par> ::= <Decl_par>[,<Decl_par>] .. [, ..]

<Decl_par> ::= <Tip>..<Declarator><Decl_tip>

<Decl_tip> ::= <Tip>..[<Indirect>..][(<Decl_tip>)][<Decl_F_T>]

<Iniţializare> ::= =<Expr> ={<Listă_init>[,]}

<Listă_init>::=<Expr>[,<Listă_init> ..{<Listă_init>}[,<Listă_init>] ..

<Def_funcţie> ::= [<Specif_funcţie> ..]<Declarator>[<Declaraţie>;] ..<Bloc>

<Specif_funcţie> ::= externstatic<Tip>

C. Expresii<Expr> ::= <Expresie>[,<Expresie>] ..

<Expresie> ::= <Expr_condiţională>|<Expr_unară><Oper_atribuire><Expresie>

<Oper_atribuire> ::= +*=/=%=+=-=<<=>>=&=^=!=

<Expr_condiţională> ::= <Expr_SAU_logic><Expr_SAU_logic> ? <Expr> : <Expr_condiională>

<Expr_SAU_logic> ::= <Expr_ŞI_logic><Expr_SAU_logic> !! <Expr_ŞI_logic>

<Expr_ŞI_logic> ::= <Expr_SAU><Expr_ŞI_logic> && <Expr_SAU>

<Expr_SAU> ::= <Expr_SAU_exclusiv><Expr_SAU><Expr_SAU_exclusiv> <Expr_SAU_exclusiv> ::= <Expr_ŞI><Expr_SAU_exclusiv> ^ <Expr_ŞI>

<Expr_ŞI> ::= <Expr_egalitate><Expr_ŞI> & <Expr_egalitate>

<Expr_egalitate> ::= <Expr_relaională><Expr_egalitate>==Expr_relaţională>

<Expr_egalitate> != <Expr_relaţională>

<Expr_relaţională> ::= <Expr_deplasare> <Expr_relaţională> < <Expr_deplasare>

<Expr_relaţională> > <Expr_deplasare> <Expr_relaţională> <= <Expr_deplasare>

<Expr_relaţională> >= <Expr_deplasare>

<Expr_deplasare> ::= <Expr_aditivă> <Expr_deplasare> << <Expr_aditivă> <Expr_deplasare> >> <Expr_aditivă>

- 105 -

Page 106: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

<Expr_aditivă> ::= <Expr_multiplicativă><Expr_aditivă> + <Expr_multiplicativă>

<Expr_aditivă> - <Expr_multiplicativă>

<Expr_multiplicativă>::= <Expr_multiplicativă> * <Expr_prefixată> <Expr_multiplicativă> / <Expr_prefixată>

<Expr_multiplicativă> % <Expr_prefixată>

<Expr_prefixată> ::= <Expr_unară>(<Decl_tip>)<Expr_prefixată>

<Expr_unară> ::= <Expr_postfixată><Op_unar><Expr_prefixată> ++<Expr_unară> --<Expr_unară>sizeof<Expr_unară> sizeof(<Decl_tip>)

<Op_unar> ::= &*+-~!

<Expr_postfixată> ::= <Termen><Expr_postfixată>[<Expr>]<Expr_postfixată>(<Listă_Expresii>) <Expr_postfixată> . <Nume>

<Expr_postfixată> -> <Nume><Expr_postfixată> ++ <Expr_postfixată>--

<Termen> ::= <Nume><Constantă><Şir_de_caractere>(<Expr>)<Listă_Expresii> ::= [<Expr>] ..

<Expr_constantă> ::= <Expr_condiţională>

D. Instrucţiuni

<Instr>::= <Instr_etichetată><Instr_compusă> <Instr_expresie>Instr_de_selecţie>

<Instr_de_ciclare><Instr_de_salt>

<Instr_etichetată> ::= <Nume>:<Instr>case <Expr_constantă { :<Instr> default : <Instr>

<Instr_compusă> ::= <Bloc>

<Bloc> ::= {[<Declaraţie>;] .. [<Instr>] ..}

<Instr_expresie> ::= [<Expr>];

<Instr_de_selecţie> ::= if (<Expr>) <Instr>if (<Expr>) <Instr> else <Instr>

switch (<Expr>) <Instr>

<Instr_de_ciclare> ::= while (<Expr>)<Instr>; do <Instr> while (<Expr>); for ( [<Expr>];[<Expr>];[<Expr>] ) [<Instr>];

<Instr_de_salt> ::= goto <Nume>;continue;break;return [<Expr>];

- 106 -

Page 107: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

BORLAND C – Manual de utilizare

- 107 -

Page 108: BORLAND C – Manual de utilizare · BORLAND C – Manual de utilizare ... int modul (int i) // determina ... Important Limbajul C/C++ este “case sensitive” adica un identificator

CUPRINS

BIBLIOGRAFIE

Brian W.Kerninghan, Dennis M. Ritchie, The C Programming Language, INC. Englewood Cliffs, New Jersey, 1978.

Liviu Negrescu, Limbajul C, Editura Libris, Cluj-Napoca, 1997.

G. Moldovan, M. Lupea, V.Cioban, Probleme pentru programare în limbajul C, Litografia Universităţii Babeş-Bolyai, Cluj-Napoca, 1995.