Documente Academic
Documente Profesional
Documente Cultură
Un cuvânt cheie este un cuvânt împrumutat din limba engleză, care are un înţeles
predefinit. Cuvinte cheie se scriu cu litere mici în limbajul C.
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 totodată şi un nume.
Exemple: if while for break do
Lista cuvintelor cheie este dată în anexa I.
Sensul fiecărui cuvânt cheie va rezulta la definirea construcţiei în care se utilizează.
Cuvânt Lungime
Tip de reprezentare internă
cheie în biţi
int 16 întreg binar reprezentat prin complement faţă de 2 pe 2 octeţi
short 16 idem
long 32 întreg binar reprezentat prin complement faţă de 2 pe 4 octeţi
unsigned 16 întreg binar fără semn
char 8 caracter reprezentat prin cod ASCII
float 32 număr reprezentat în virgulă flotantă în simplă precizie
double 64 număr reprezentat în virgulă flotantă în dublă precizie
long double 80 număr reprezentat în virgulă flotantă în dublă precizie
Datele de tip int (întregi cu semn) aparţin intervalului [-32768, 32767]. Datele de tip
long (întregi cu semn în dublă precizie) aparţin intervalului [-231, 331]. Datele de tip unsingned
(întregi fără semn) aparţin intervalului [0,65535]. Datele de tip char au valori în intervalul
[0,255] sau [-128,127]. Se pot utiliza întregi fără semn în dublă lungime folosind succesiunea
de cuvinte cheie: unsigned long. O dată flotantă în simplă precizie de tip float, diferă de zero,
Limbajul de programare C 136
-38 38
are valoarea absolută în intervalul [3.4*10 , 3.4*10 ]. O dată flotantă în dublă precizie de tip
double, long double în intervalul [3.4*10-4932, 1.1*104932].
6.2.1.4. Constante
O contantă are o valoare şi un tip. Atât tipul cât şi valoarea unei constante se definesc
prin caracterele care compun constanta respectivă.
a) Constante întregi
O constantă întreagă poate fi un şir de cifre, care eventual este precedat de un semn. O
constantă întreagă se reprezintă prin complement faţă de 2 pe 16 biţi sau chiar 32 de biţi dacă
nu încape pe 16 biţi. În cazul în care dorim ca o constantă întreagă să fie reprezentată pe 32 de
biţi, chiar dacă ea se poate reprezenta pe 16 biţi, vom termina constanta respectivă prin L sau l.
În felul acesta, se impune tipul long pentru constanta respectivă.
O constantă întreagă, precedată de un zero nesemnificati, se consideră scrisă în
sistemul de numeraţie cu baza 8. De asemenea, o constantă întreagă care începe cu 0x sau 0X
(zero urmat de litera x mică sau mare) se consideră scrisă în sistemul de numeraţie cu baza 16.
În rest se consideră că baza de numeraţie este 10.
Exemple de reprezentări sunt arătate în tabelul 6.2.
Tabelul 6.2.
Repreze Lungimea
Lungime de reprezentare internă
ntare reprezentării
31645 16 biţi întreg zecimal reprezentat în binar prin complement faţă de 2
-12345 16 biţi întreg zecimal reprezentat în binar prin complement faţă de 2
12345L 32 biţi întreg zecimal reprezentat în binar prin complement faţă de 2
întreg octal reprezentat în binar
012345 16 biţi
(o cifră octală se reprezintă pe 3 biţi)
întreg hexazecimal reprezentat în binar
0xa12b 16 biţi
(o cifră hexazecimală se reprezintă pe 4 biţi)
923456 32 biţi întreg zecimal reprezentat în binar prin complement faţă de 2
c) Constantă caracter
O constantă şir este o succesiune de zero sau mai multe caractere delimitate prin
ghilimele. Ghilimelele nu fac parte din şirul de caractere. Dacă dorim să folosim caractere
negrafice în compunerea unui şir de caractere, atunci putem folosi convenţia de utilizare a
caracterului backslash indicată mai sus. În particular, dacă într-un şir de caractere dorim să
reprezentăm chiar caracterul ghilimele, atunci vom scrie: \”. De asemenea, însuşi caracterul
backslash se va scrie: \\.
Prin variabilă înţelegem o dată a cărei valoare se poate schimba în timpul executării
programului care o conţine.
Limbajul de programare C 138
Unei variabile i se ataşează un nume prin intermediul căruia putem referi sau chiar
modifica valoarea variabilei respective. Valorile pe care le poate avea o variabilă trebuiesc să
aparţină unui acelaşi tip. De aceea, unei variabile îi corespunde un tip.
6.2.1.6. Comentarii
6.2.2. Expresii
6.2.2.1. Structura expresiilor în limbajul C
O expresie în limbajul C se compune dintr-un operand sau mai mulţi operanzi legaţi
prin operatori.
O expresie are o valoare şi un tip care se determină aplicând operatorii prezenţi
conform priorităţilor şi asociativităţii acestora.
În limbajul C operatorii se asociază de la stânga la dreapta, exceptând operatorii unari,
condiţionali şi de atribuire, care se asociază de la dreapta la stânga.
Într-o expresie pot fi folosite parantezele rotunde pentru a impune o anumită ordine în
executarea operaţiilor.
Operatorii limbajului C pot fi grupaţi în mai multe clase. Cu toate acestea ei pot fi
utilizaţi împreună într-o aceeaşi expresie.
Aceştia sunt:
139 Limbajul de programare C
- (minus unar);
+ (plus unar);
* / % operatori binari multiplicativi;
+ - operatori binari aditivi.
Aceştia sunt:
< (mai mic);
<= (mai mic sau egal);
> (mai mare);
>= (mai mare sau egal).
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:
Fie a = 3 şi b = -8
Atunci:
a>0 are valoarea 1; a<=0 are valoarea 0;
a+b>0 are valoarea 0; a>=0 are valoarea 1;
-a<0 are valoarea 1; a+b>=b-a are valoarea 1;
a+b<=b-a are valoarea 0.
Operatorul “&&” (ŞI logic) este mai prioritar decât operatorul “” (SAU logic), 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.
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ă.
Exemplu:
an%4= = 0 && an% 100! = 0 || an% 400 = = 0.
Variabila an, din expresia de mai sus, are valoarea unui întreg ce reprezintă un an
calendaristic. Expresia are valoarea 1 dacă anul reprezentat prin valoarea variabilei an este
bisect şi zero în caz contrar.
Într-adevăr, un an este bisect dacă are loc una din următoarele condiţii:
a) anul este multiplu de 4 şi nu este multiplu de 100; sau
b) este multiplu de 400.
Condiţia a) se exprimă prin: multiplu de 4: an%4 = = 0
şi: &&
nu e multiplu de 100: an%100! = 0
Condiţia b) se exprimă prin: multiplu de 400: an%400 = = 0
În final cele două condiţii se leagă prin operatorul “||” (SAU) şi în felul acesta se obţine
expresia cerută.
Operatorul ~ are ca effect inversarea tuturor biţilor: biţii care iniţial au avut valoarea 1
vor deveni 0, iar cei care au avut valoarea 0 vor deveni 1.
Operatorul << este un operator binar pentru deplasare la stânga care se aplică astfel:
141 Limbajul de programare C
a << b va avea ca efect deplasarea la stânga a biţilor lui a cu b poziţii.
Operatorul >> este un operator binar pentru deplasare la dreapta care se aplică astfel:
a >> b va avea ca efect deplasarea la dreapta a biţilor lui a cu b poziţii.
Exemple de utilizare a operatorilor pe biţi:
Considerăm declaraţia: int x=6, y=3;
unde reprezentările interne ale celor două numere întregi sunt următoarele:
x 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0
y 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
~x 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1
care are valoarea în hexazecimal fff9 iar în zecimal are valoarea –7.
2. Aplicând operatorul & celor două numere întregi x, y se va obţine expresia:
x&y 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
x|y 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
x<<y 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0
care în hexazecimal are valoarea 30 iar în zecimal are valoarea 48.
5. Aplicând operatorul >> celor două numere întregi x şi y se obţine valoarea:
x>>y 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
care are valoarea 0.
Aplicarea operatorului de deplasare la stânga, << , este echivalentă cu o înmulţire a lui
x cu 2 , iar a operatorului de deplasare la dreapta, >>, cu o împărţire a lui x cu 2y.
y
X= 0 1 1 0 0 1 0 1
C1 C2
Limbajul de programare C 142
C1 = x >> 4, iar
C2 = x & 0x0f
Operatorul de atribuire în forma cea mai simplă se notează prin caracterul “=”. El se
utilizează în construcţii de forma:
v = expresie.
Operatorul de atribuire are prioritatea cea mai mică faţă de operatorii indicaţi până în
momentul de faţă.
Construcţia v = expresie se numeşte expresie de atribuire. Ea este considerată ca fiind
un caz particular de expresie. Ca orice expresie, ea are o valoare şi un tip. Tipul ei coincide cu
tipul lui v, iar valoarea întregii expresii este chiar valoarea atribuită lui v. Rezultă de aici că o
expresie de forma v1 = (v = expresie) este şi ea legală.
Operatorii de atribuire se asociază de la dreapta la stânga. În general, putem realiza
atribuiri multiple printr-o expresie de forma:
vn = … = v1 = v = expresie.
În evaluarea unei expresii se aplică regula conversiilor implicite. Prin aplicarea ei
rezultă fiecare expresie de tip. În cazul unei expresii de atribuire, dacă 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 apoi 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 dintre operatorii binari aritmetici sau logici pe biţi, adică
unul din următorii: % / * - + & ^ | << >>.
Aceşti operatori se folosesc pentru a prescurta expresiile de atribuire. Astfel expresia:
v op= expresie este identică cu expresia de atribuire: v = v op (expresie).
Exemple:
Fie declaraţiile: int i,j,k; double x,y;
x = 3.8 - se atribuie lui x valoarea 3,8;
i = 3.8 - se atribuie lui i valoarea 3;
i = x>0 - se atribuie lui i valoarea 1 dacă x>0 şi 0 în caz contrar;
x = (i*j+k)/(i*j-k)
x* = 3 - este echivalent cu x = x*3;
i<<=10 - este echivalent cu i = i<<10;
x*=y-10 - este echivalent cu x=x*(y-10)
i& = j>>k - este echivalent cu i=i&( j>>k).
143 Limbajul de programare C
Construcţiile de mai jos sunt eronate:
x%=y - operatorul % nu se poate aplica la operanzi care nu sunt
întregi.
i∼ =j - construcţia op= implică un operator binar;
x=i+(j*(k-y)+2.3 - lipseşte o paranteză închisă;
y=3(x-10) - lipseşte operatorul * după 3.
Aceşti operatori sunt unari şi deci au aceeaşi prioritate cu ceilalţi operatori unari din
limbajul C. Operatorul de incrementare se notează prin “++”, iar cel de decrementare prin
“--“.
Operatorul de incrementare măreşte valoarea operandului său cu unu, iar cel de
decrementare micşorează valoarea operandului cu unu. Operatorii pot fi astfel :
prefixaţi: ++ operand -- operand
postfixaţi: operand ++ operand --
În cazul în care sunt folosiţi prefixaţi, ei se aplică întâi şi apoi se foloseşte valoarea
operandului la care s-a aplicat. Când sunt postfixaţi, se foloseşte valoarea operandului
nemodificată de operatorul respectiv, apoi se aplică.
Exemple:
Dacă x are valoarea 3, atunci în atribuirea y= ++x lui y i se atribuie valoarea 4 deoarece
întâi s-a incrementat x şi apoi s-a folosit valoarea x. În schimb, dacă folosim atribuirea y=x++,
pentru aceeaşi valoare a lui x, atunci lui y i se atribuie valoarea 3. În ambele cazuri x se
măreşte cu 1. Diferenţa constă numai în valoarea atribuită: în primul caz (operator prefixat),
se atribuie valoarea incrementată, iar în al doilea caz (operator postfixat), se atribuie valoarea
neincrementată.
Menţionăm că aceşti operatori se pot aplica numai la următorii operanzi: variabilă
simplă cu indici, referire la elementul unei structuri (structurile vor fi definite mai târziu).
Exemple:
int i,j; double x,y;
j=i++ - este echivalent cu atribuirile j=i; i=i+1;
y= -- x - este echivalent cu secvenţa de atribuire: x=x-1; y=x;
x=tab[3]-- - este echivalent cu: x= tab[3]; tab[3]=tab[3]-1;
i=++tab[j+1] - este echivalent cu: tab[j+1]=tab[j+1]+1; i=tab[j+1];
x=tab[++j] - este echivalent cu: j=j+1; x= tab[j];
x = tab[i--] - este echivalent cu: x=tab[i]; i=i-1;
y=++i-j - este echivalent cu: i=i+1;y=i-j;
y=i++-j - este echivalent cu: y=i-j; i=i+1;
Construcţiile următoare sunt eronate:
y=(i-j)++ - operatorul ++ nu se poate aplica expresiei i-j;
Limbajul de programare C 144
x= --(i-j) - operatorul – nu se poate aplica expresiei i-j;
i=j- - - “- -“ nu mai reprezintă operatorul de decrementare.
Adesea dorim să forţăm tipul unui operand sau chiar al unei expresii. Acest lucru este
posibil folosind o construcţie de forma: (tip) operand.
O expresie poate conţine operanzi de tipuri diferite. Nu există restricţii în acest sens.
Astfel, de exemplu într-o expresie se pot folosi operanzi de tip char. Aceştia se convertesc în
mod automat spre tipul int, înainte de a face operaţii cu ei.
Dacă operanzii unui operator binar sunt de acelaşi tip, se aplică operatorul asupra
operanzilor respectivi, iar tipul rezultatului este acelaşi cu al operanzilor. În caz contrar
sunt necesare conversii care se execută în mod automat, conform regulii de mai jos
(regula conversiilor implicite):
Există cazuri în care este util să grupăm mai multe expresii într-una singură, expresii
care să se evalueze succesiv.
În acest scop se foloseşte operatorul virgulă, care separă secvenţa de expresii, acestea
grupându-se într-o singură expresie.
Operatorul virgulă are cea mai mică prioritate dintre toţi operatorii limbajului C.
Prioritatea lui este imediat mai mică decât a operatorilor de atribuire.
Cu ajutorul lui construim expresii de forma: exp1, exp2,…,expn .
Această expresie, ca oricare alta, are o valoare şi un tip. Atât valoarea, cât şi tipul
întregii expresii coincide cu valoarea şi tipul lui expn deci cu a ultimei expresii.
Exemple:
++i,--j - i se măreşte cu o unitate, apoi j se micşorează cu o
unitate; valoarea şi tipul întregii expresii coincid cu ale lui j;
k=(x=10,y=2*i-5,z=3*j,i+j) - se execută pe rând cele 3 atribuiri, apoi se efectuează
suma i+j care se atribuie lui k.
Amintim că toţi operatorii se asociază de la stânga la dreapta, exceptând cei unari,
condiţionali şi de atribuire care se asociază de la dreapta la stânga. În tabelul 6.4 sunt
prezentate priorităţile operatorilor.
Tabelul 6.4
( ) [ ] .
-(unar) +(unar) *(unar) &(unar) ! ∼ ++ -- (tip) sizeof
* / %
+ -
<< >>
< <= >= >
== !=
&
^
&&
? :
= op= op poate fi: (binar) / % +(binar) -(binar) >> << & ^ !
,
Limbajul de programare C 148
6.3. Structura programelor C
Un program în limbajul C se compune din una sau mai multe funcţii. Dintre acestea
una este funcţia principală. Funcţia principală defineşte adresa de lansare a programului. Un
program în C se lansează cu prima instrucţiune a funcţiei principale.
Fiecare funcţie are un nume. Funcţia principală are numele main.
În limbajul C există două feluri de funcţii: funcţii care returnează o valoare şi funcţii
care nu returnează o valoare la revenirea din ele. Structura unei funcţii este următoarea:
tip nume (lista parametrilor formali)
{
declaraţii de variabile locale
instrucţiuni
}
unde:
- tip defineşte tipul valorii returnate de funcţie şi este un cuvânt cheie pentru tipurile de
bază. Dacă funcţia nu returnează nici o valoare, se poate utiliza cuvântul cheie void. Dacă tip
este absent, se presupune că funcţia sau nu returnează nici o valoare sau returnează o valoare
de tip int. Se recomandă ca utilizatorul să indice totodată tipul deoarece absenţa lui constituie
o sursă potenţială de erori.
- lista_parametrilor_formali este fie vidă, fie conţine declaraţiile parametrilor formali
separate prin virgulă.
Menţionăm că parantezele rotunde sunt prezente chiar şi atunci când lista
parametrilor formali este vidă.
6.4. Preprocesare
Un program în limbajul C poate fi prelucrat înainte de a fi compilat. O astfel de
prelucrare se numeşte preprocesare.
Prin preprocesare se pot realiza:
- includeri de texte;
- definiţii şi apeluri de macrouri simple;
- compilare condiţionată;
Astfel, varianta <...> specifică includerea unui fişier standard, care este căutat de
obicei în directorul INCLUDE.
În cazul în care se utilizează caracterele ghilimele, utilizatorul furnizează fişierul care
se include. Acest fişier va fi căutat în directorul curent sau într-un director precizat.
Un fişier standard care trebuie inclus frecvent este fişierul stdio.h. Includerile de
fişiere, de obicei, se fac la începutul fişierului sursă. În felul acesta datele conţinute în el se pot
utiliza în tot fişierul sursă. De aceea, la începutul fişierelor sursă vom întâlni mai multe
includeri de fişiere, printre care de obicei se va afla şi fişierul stdio.h: #include <stdio.h>.
Un exemplu de închidere a unui fişier utilizator este: #include “mouse.h”.
1.Un caracter “-“ opţional: implicit, datele se aliniază în dreapta câmpului în care se
scriu. Atunci când caracterul “-“ este prezent, data corespunzătoare este încadrată la stânga.
2.Un şir de cifre zecimale opţionale, care defineşte dimensiunea minimă a câmpului
afectat datei respective.
3.Un punct opţional, urmat de un şir de cifre zecimal. Şirul de cifre zecimal aflat după
punct defineşte precizia datei care se scrie sub controlul specificatorului respectiv.
4.Una sau două litere, care definesc tipul de conversie aplicat datei care se scrie. În
cazul în care specificatorul de format conţine două litere, prima poate fi l.
În tabelul 6.5 sunt prezentate intrările/ieşirile standard utilizate în limbajul C.
Tabelul 6.5.
Litera Conversia realizată
data se converteşte din tipul int şi zecimal şi se scriu la ieşire caracterele zecimale
d
ale ei, eventual precedate de semnul “-“, dacă este negativă.
o data se converteşte din tipul int în octal şi se scriu la ieşire caracterele ei octale;
data se converteşte din tipul int în hexazecimal şi se scriu la ieşire caracterele ei
x
hexazecimale; cifrele peste 9 se scriu cu literele mici (a-f);
X ca şi în cazul literei x, dar se vor folosi literele mari (A-F);
u data se converteşte din tipul usingned în zecimal întreg fără semn;
valoarea parametrului care îi corespunde se consideră că reprezintă codul ASCII al
c
unui caracter şi se scrie caracterul respectiv;
parametrul care-i corespunde se scrie ca un şir de caractere; se consideră că şirul se
s
termină la întâlnirea caracterului NUL (‘\0’);
valoarea parametrului care-i corespunde se converteşte din tip float sau double în
formatul: dd ...d.dd…d (d reprezintă o cifră zecimală), unde numărul de cifre după
f
punctul zecimal este fie cel indicat de precizie specificatorului de format, fie este
egal cu 6; partea întreagă este precedată de semnul minus dacă numărul este negativ;
conversia se realizează de tipul float sau double în formatul:
d.dd..de± ddd unde numărul cifrelor de după punctul zecimal este dat de precizia
e
specificatorului de format sau este egal cu 6, dacă acesta este absent; partea întreagă
este precedată de minus dacă numărul este negativ;
151 Limbajul de programare C
ca şi în cazul literei e cu deosebirea că litera e se schimbă cu litera E:
E d.dd..dE± ddd în ambele cazuri, la exponent se va scrie una, două sau trei cifre, în
funcţie de valoarea numărului;
se aplică una din conversiile definite de literele f sau e, alegându-se aceea care se
g reprezintă pe un număr minim de caractere; de asemenea, zerourile de la sfârşitul
părţii fracţionare se omit;
G ca şi g cu singura deosebire că se utilizează E în loc de e.
Funcţia printf returnează lungimea totală în octeţi a datelor scrise la terminal sau
valoarea simbolică EOF în caz de eroare. EOF este o constantă simbolică definită în fişierul
stdio.h. Ea este definită astfel: #define EOF – 1.
Exemple:
Fie declaraţiile: int i,j; float a; double x; int c; long k;
i = 558 a = 47.389 x = -123.5e20 c = ‘x’ j = -123 k = 45678
Mai jos apelăm funcţia printf cu diferiţi specificatori de format care permit afişarea la
terminal a valorilor acestor variabile. În tabelul 6.6 sunt prezentate rezultatele afişate în cazul
apelării funcţiei printf:
Tabelul 6.6.
Exemplul 2:
Programul următor citeşte o dată calendaristică scrisă sub forma: zzllaaaa
zz - ziua cu 2 cifre;
ll - luna cu 2 cifre;
aaaa - anul cu 4 cifre.
apoi se scrie data respectivă permutând anul cu ziua, astfel încât dacă la intrare se citeşte, spre
exemplu, 01031992, la ieşire se va obţine 19920301.
Exemplul 3:
#define MAX 50
void main () /* citeşte nume, prenume şi data naşterii şi rescrie datele respective astfel:
linia 1: nume prenume
linia 2: zi luna an */
{ int ziua, luna, an;
char nume [MAX+1], prenume [MAX+1];
scanf (“%50s%50s%d%d%d”, nume, prenume, &zi, &luna, &an);
153 Limbajul de programare C
printf(“%s%s\n”, nume, prenume);
printf(“%d %d %d\n”, zi, luna, an);
}
Exemplu de linie de intrare: Popescu Ion 1 9 1991
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.
Exemple:
1) putchar (‘A’);
- se scrie caracterul A în fişierul de ieşire în poziţia curentă a cursorului;
2) putchar (‘A’+10);
- se scrie caracterul de cod ‘A’+10 = 65+10=75, adică litera K;
3) putchar (‘\n’);
- se scrie caracterul de rând nou (newline). Aceasta are ca efect deplasarea
cursorului în coloana 1 din linia următoare.
Această funcţie citeşte de la intrarea standard (fişierul 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 (^Z) se returnează valoarea EOF.
Funcţia getchar poate fi apelată printr-o instrucţiune de forma:
getchar()
sau utilizând-o ca operand într-o expresie. Folosind expresia de atribuire:
c =getchar ()
se citeşte caracterul curent de la intrare.
Funcţia gets returnează adresa de început a zonei în care s-au păstrat caracterele sau
zero în cazul în care s-a întâlnit sfârşitul (^Z).
Exemplu:
char t[255]; gets(t);
La acest apel se citesc caracterele tastate pe un rând şi se păstrează în tabloul t.
Caracterul tastat înainte de a acţiona tasta ENTER este urmat în tabloul t de caracterul NUL,
iar la revenirea din funcţie se returnează adresa zonei receptoare adică valoarea lui t.
Funcţia puts realizează operaţia inversă faţă de funcţia gets. Ea afişează la terminal
caracterele şirului de caractere ASCII aflate într-o zonă de memorie. Adresa de început a
acestei zone de memorie este parametrul funcţiei puts.
Exemplu:
char t[255]; gets(t); puts(t);
Se dă funcţia y(x) definită ca mai jos:
3 x 2 + 2 x − 10 pentru x < 0
y( x ) =
5 x + 2 pentru x ≥ 0
Să se scrie un program care citeşte valoarea variabilei x (virgulă flotantă dublă
precizie) şi scrie valoarea lui y.
Programul este prezentat în continuare:
#include <stdio.h>
#include <conio.h>
void main ()
{ float x,y;
printf("\nIntroduceti valoarea lui y:");
scanf("%f",&x);
y=x<0?3*x*x+2*x-10:5*x+2;
printf("\nValoarea functiei este %f",y);
getch();
}
Pentru citirea şi scrierea datelor de tip float s-a utilizat specificatorul de format %f.
6.6.3. Instrucţiunea IF
Instrucţiunea if permite să realizeze o ramificare a execuţiei în funcţie de valoarea unei
expresii. Ea are unul din următoarele formate:
Format 1:
if(expresie) instrucţiune 1
Format 2:
if(expresie) instrucţiune 1
else instrucţiune 2
Exemple:
1. Programul următor citeşte trei numere întregi şi afişează maximul dintre ele.
void main () /* citeşte a,b,c şi scrie maximul dintre ele */
{
int a,b,c, max;
scanf (“%d%d%d”, &a, &b, &c);
if (a > b) max = a;
else max = b;
if (c > max) max = c;
printf (“max(%d, %d, %d) = %d\n”, a, b, c, max);
}
2. Programul următor citeşte valoarea lui x şi scrie valoarea funcţiei de mai jos:
Limbajul de programare C 156
4 x 3 + 5 x 2 − 2 x + 1 pentru x < 0
y ( x ) = 100 pentru x = 0
2
2 x + 8 x − 1 pentru x > 0
Observaţie:
Instrucţiunile din corpul ciclului while:
f = f*i;
i++;
pot fi înlocuite cu una singură: f*=i++; Deci ciclul while se poate scrie mai compact astfel:
while(i<=n) f*=i++;
3. Programul următor citeşte un şir de numere întregi şi scrie maximul dintre ele. Se
presupune că şirul conţine cel puţin un număr.
void main () /* citeşte un şir de numere întregi şi scrie maximul dintre ele */
{
int max,i;
scanf(“%d”, &max);
while (scanf(“%d, &i) = =1) if(i > max) max = i;
printf(“max = %d\n”, max);
}
Un exemplu foarte simplu de ciclu cu pas este însumarea elementelor unui tablou.
s = 0;
for (i=0; i<n; i++) s = s + tab[i];
sau mai compact:
for (s=0, i=0; i<n; i++) s+=tab[i];
În secvenţa compactă s-a introdus expresia s=0 în pasul de iniţializare a ciclului
folosind operatorul virgulă.
Aceeaşi secvenţă se poate scrie folosind instrucţiunea while:
s=0; i=0;
while (i<n)
{
s+=tab[i];i++;
}
sau mai compact:
s = 0; i = 0;
while (i<n) s += tab [i++];
Exemple:
do
instrucţiune
while (expresie);
Aceasta, la rândul ei, poate fi scrisă printr-o secvenţă în care se foloseşte instrucţiunea
while:
instrucţiune
while (expresie) instrucţiune
Exemple:
1. Programul următor utilizează o metodă iterativă simplă pentru extragerea rădăcinii
pătrate dintr-un număr. Fie şirul:
x0, x1, ... xn,
unde xn=1/2(xn-1+a/xn-1)
Se poate demonstra că acest şir converge, pentru a>0, către rădăcina pătrată din a.
Convergenţa este rapidă pentru 0<a<=1. În acest caz se poate lua x0 = 1. Pentru a obţine
rădăcina pătrată cu o precizie dată, este suficient ca diferenţa absolută dintre doi termeni
consecutivi ai şirului să fie mai mică decât eroarea admisă EPS. Programul de faţă consideră
EPS = 10-10.
Pentru realizarea procesului iterativ descris de mai sus, sunt suficiente două variabile
x1 şi x2 care păstrează doi termeni consecutivi. Programul în limbajul C este:
#define EPS 1e-10
void main () /* calculeaza rădăcina pătrată dintr-un număr subunitar */
{
double x1, x2, y, a;
if (scanf (“%1f”, &a) !=1 || a<0 || a > 1.0)
printf (“numarul citit nu este subunitar si pozitiv\n”);
else {
x2 = 1.0;
do {
x1 = x2; x2 = 0.5*(x1+a/x1);
if ((y = x2-x1)<0) y=-y;
}
while (y >= EPS);
printf (“radacina patrata din: %g este: %.11f\n”, a, x2);
} /* sfârşit else */
}
161 Limbajul de programare C
6.6.7. Instrucţiunea SWITCH
switch (expresie)
{
case c1: şir1 break;
case c2: şir2 break;
………………………
case cn: şirn break;
default: şir
}
unde: c1, c2, …, cn sunt constante sau constante simbolice;
şir1, şir2, …, şirn sunt şiruri de instrucţiuni;
Exemple:
1. Programul următor citeşte o dată calendaristică de forma: zzllaaaa şi o scrie sub
forma: aaaa, luna zi unde:
zz este ziua pe 2 cifre; aaaa este anul pe 4 cifre;
ll este luna pe 2 cifre; zi este ziua pe 1-2cifre;
luna este denumirea lunii calendaristice;
În acest caz declararea unei variabile simple cum este variabila “temperatura” din
exemplele anterioare nu mai este suficientă, deoarece ea poate memora la un moment dat doar
temperatura curentă, nu şi pe celelalte temperaturi.
În astfel de situaţii se utilizează date structurate. Datele structurate sunt date care
conţin mai multe date de acelaşi tip sau de tipuri diferite. Tablourile sunt structuri de date care
conţin date de acelaşi tip. Referirea la o dată din cadrul tabloului se face utilizând un indice.
Indicele reprezintă poziţia datei în tablou.
Un tablou, ca orice variabilă simplă, trebuie declarat înainte de a fi utilizat. Declaraţia
de tablou, în forma cea mai simplă, conţine tipul comun, al elementelor sale, numele tabloului
şi limitele superioare pentru fiecare indice, incluse între paranteze drepte:
tip lista_de_elemente;
unde lista_de_elemente se compune dintr-un element sau mai multe, separate prin virgulă. Un
element dintr-o astfel de listă are formatul: nume[lim1][lim2]…[limn] în care lim1, lim2, …,
limn sunt expresii constante care au valori întregi. Prin expresie constantă înţelegem o
expresie care poate fi evaluată la întâlnirea ei de către compilator.
Exemple:
1.Fie v un vector cu 10 componente de tip întreg. El se declară astfel: int v[10];
2.Fie a o matrice cu 100 de linii şi 4 coloane. Elementele ei sunt reprezentate în virgulă
flotantă simplă precizie. Ea se declară astfel: float a[100] [4];
La elementele unui tablou ne referim prin variabile cu indici. O astfel de variabilă se
compune din numele tabloului urmat de unul sau mai mulţi indici, fiecare indice 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ă v este tabloul declarat în exemplul 1, atunci elementele lui sunt: v[0], v[1],
v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9].
În cazul matricei a declarate în exemplu 2, elementele ei vor fi
pe prima linie: a[0] [0], a[0] [1], a[0] [2], a[0] [3]
pe a doua linie: a[1] [0], a[1] [1], a[1] [2], a[1] [3]
...
pe ultima linie: a[99] [0], a[99] [1], a[99] [2], a[99] [3]
165 Limbajul de programare C
Numele unui tablou poate fi utilizat în diferite construcţii, el având ca valoare adresa
primului său element.
În figura 6.1 se indică repartizarea memoriei pentru tabloul tab, declarat mai sus:
Adresa lui
tab[0] tab[0] tab[1] tab[2] tab [3]
2. Se consideră o matrice cu „n” linii şi „m” coloane care are elemente de tip real. Se
cere programul C care determină toate elementele din matrice, împreună cu poziţiile lor, care
au proprietate de a fi maxime pe linia în care se află şi minime pe coloana în care se află.
Programul va conţine şi secvenţa de afişare a matricei date.
Spre exemplu, pentru matricea următoare:
2 14 1 9 11
−8 0 3 6 1
2 −1 3 7 4
6 10 1 11 1
un astfel de element este „6” aflat la intersecţia liniei „2” cu coloana „4”.
Problema poate fi rezolvată în felul următor: Se determină pentru fiecare linie
elementul maxim şi pentru fiecare coloană elementul minim. Aceste valori se păstrează în câte
un vector cu numele „max”, respectiv „min”. După determinarea elementelor acestor doi
vectori, ele se vor compara între ele pentru determinarea elementului căutat. Pentru matricea
din exemplul anterior cei doi vectori vor fi:
2 14 1 9 11 14
− 8 0 3 6 1 6
2 −1 ⇒
3 7 4 7
6 10 1 11 1 11
⇓
(− 8 −1 1 6 1)
Programul este prezentat în continuare:
#include <stdio.h>
#include<conio.h>
void main()
{float a[10][10],min[10],max[10];
int i,j,m,n;
printf("\nIntroduceti nr. de linii:");
scanf("%d",&n);
167 Limbajul de programare C
printf("\nIntroduceti nr. de coloane:");
scanf("%d",&m);
printf("\nIntroduceti elementele matricei\n");
for (i=0;i<n;i++)
for(j=0;j<m;j++)
{ printf("\na[%d][%d]=",i,j);
scanf("%f",&a[i][j]); }
printf("\nMatricea data este:\n");
3. Se cere un program care citeşte de la tastatură elementele întregi ale unui vector de
dimensiune „n” şi afişează aceste elemente în ordine crescătoare.
Problema are o importanţă destul de mare în programare, deoarece problema sortării
crescătoare sau descrescătoare a unui şir de valori este destul de frecvent întâlnită în practică.
S-au descoperit un număr mare de algoritmi de sortare, dintre care se va utiliza un algoritm
Limbajul de programare C 168
relativ simplu, algoritm cunoscut sub denumirea de „bubblesort” sau sortare prin metoda
bulelor.
Algoritmul constă din parcurgerea vectorului de mai multe ori, în fiecare parcurgere
comparându-se elementele aflate pe poziţii alăturate. Ori de câte ori cele două elemente
comparate nu se află în ordinea dorită, ele se vor interschimba. Pentru interschimbare se va
folosi o variabilă auxiliară „aux”. Algoritmul se consideră încheiat în urma unei parcurgeri a
vectorului în care nu se mai efectuează nici o interchimbare. Pentru a se marca efectuarea unei
interschimbări se va utiliza o variabilă de tip întreg, „gata”, cu rol de „adevărat”/”fals”.
Această variabilă va primi valoarea „1” înainte de fiecare parcurgere a vectorului şi valoarea
„0” în momentul în care se efectuează o interschimbare. Algoritmul se va considera încheiat
când variabila „gata” va rămâne „1” la sfârşitul parcurgerii vectorului.
b) A doua parcurgere:
gata = 1
În cea de-a treia parcurgere, care în acest caz este şi ultima, nu se mai efectuează nici o
interschimbare, prin urmare vectorul este ordonat şi algoritmul se încheie.
Denumirea algoritmului – „Sortare prin metoda bulelor” sau „Bubblesort” – provine
din analogia care se poate face între elementele vectorului şi nişte bule cu densităţi
proporţionale cu valoarea elementului respectiv. Se observă că elementele de valori mari (deci
bulele cu densităţi mai mari) coboară, cum este şi cazul elementului „9” care la început se afla
pe poziţia „0” în vector şi în urma primei parcurgeri a vectorului a coborât pe ultima poziţie.
Programul C care implementează acest algoritm este prezentat în continuare.
#include <stdio.h>
#include<conio.h>
void main()
{ int a[100],n,i,gata,aux;
printf("\nNumar elemente vector:");
scanf("%d",&n);
for (i=0;i<n;i++)
{ printf("\na[%d]=",i);
scanf("%d",&a[i]); }
do
{ gata=1;
for (i=0;i<n-1;i++) //parcurgere vector
if (a[i]>a[i+1])
{ aux=a[i]; //efectuare interschimbare
a[i]=a[i+1];
a[i+1]=aux;
gata=0; }
}
while(gata==0); //test daca s-a terminat sortarea
printf("\nVectorul sortat este:\n");
for (i=0;i<n;i++)
printf("%d ",a[i]);
Limbajul de programare C 170
getch();
}
Antet
instrucţiune compusă
Antetul are formatul:
tip nume(lista_parametrilor_formali)
Funcţiile care nu returnează o valoare la revenirea din ele au ca tip cuvântul cheie void.
De asemenea, dacă lista_parametrilor_formali este vidă, ea poate fi definită prin cuvântul
cheie void. Lista parametrilor formali conţine declaraţiile acestora separate prin virgulă.
Parametrii formali ai unei funcţii sunt reprezentaţi de datele prin care funcţia respectivă
comunică cu alte funcţii din cadrul programului respectiv. Ea se pune în corespondenţă cu lista
parametrilor efectivi din instrucţiunea de apel. Cele două liste de parametrii trebuie să coincidă
ca număr, ordine şi tip.
O funcţie poate fi apelată dacă ea este definită în fişierul sursă înainte de a fi apelată.
Acest lucru nu este totdeauna posibil şi în astfel de cazuri apelul funcţiei trebuie să fie
precedat de prototipul ei. Prototipul poate fi scris la începutul fişierului sursă şi în acest caz el
va fi valabil în tot fişierul sursă.
Prototipul unei funcţii este de fapt antetul funcţiei şi conţine toate informaţiile
referitoare la funcţia respectivă: numele funcţiei, tipul returnat de funcţie, numărul şi tipul
parametrilor funcţiei.
171 Limbajul de programare C
Exemple:
1) double val(double x, int a); Acest prototip indică faptul că funcţia val returnează o
valoare flotantă în dublă precizie şi are doi parametri, primul de tip double şi al doilea de tip
int.
Exemple de instrucţiuni de apel:
y= val(z,b); s=val(12.6, 5);
În aceste exemple s,z şi y trebuie să fie variabile de tipul „double” iar b o variabilă de
tipul „int”.
Se observă că o funcţie poate fi apelată fie cu parametrii constanţi, fie cu parametrii
variabili.
2) void citire(void);
Conform acestui prototip funcţia citire nu returnează nici o valoare şi nu are
parametrii. Funcţia poate fi apelată printr-o instrucţiune de apel de forma:
citire( );
3) double sum (double x[], int n);
Funcţia sum returnează o valoare flotantă în dublă precizie. Ea are doi parametrii: un
tablou unidimensional de tip double şi o variabilă de tip int. Această funcţie se poate apela
printr-o instrucţiune de apel de forma:
s=sum(a,m);
unde a trebuie să fie un tablou cu elemente de tip „double”, m o variabilă de tipul „int”, iar s o
variabilă de tipul „double”.
Pentru ca programul să realizeze aceste cerinţe vom defini trei funcţii utilizator:
a) O funcţie “suma_cifre” care va calcula suma cifrelor unui număr întreg transmis
ca parametru;
b) O funcţie “invers” care va returna inversul unui număr întreg transmis ca
parametru;
c) O funcţie “cmmdc” care va determina cel mai mare divizor comun a două numere
întregi transmise ca parametri.
Programul este prezentat în continuare:
#include<stdio.h>
#include<conio.h>
int suma_cifre(int n)
{ int s;
s = 0;
while (n!=0)
{ s = s + n % 10;
n = n / 10; }
return s; }
int invers(int n)
{ int inv;
173 Limbajul de programare C
inv = 0;
while (n!=0)
{ inv = inv * 10 + n % 10;
n = n / 10; }
return inv; }
int cmmdc(int a, int b)
{ while (a!=b)
if (a>b) a = a - b;
else b = b - a;
return a; }
void main()
{ int a[100],n,i,c;
float p;
printf("\nNumar elemente vector:");
scanf("%d",&n);
printf("\nElementele vectorului:\n");
for (i=0;i<n;i++)
{ printf("a[%d]=",i);
scanf("%d",&a[i]); }
O variabilă globală are o definiţie şi atâtea declaraţii de variabilă externă câte sunt
necesare.
În mod 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ă.
Exemplu:
int i; double x;
void main ()
{ …………
i = 100;
………
x = i*x;
……… }
Variabilele locale nu sunt valabile în tot programul. Exemple de variabile locale sunt
variabilele declarate în corpul unei funcţii şi care nu sunt declarate ca externe. Ele pot fi
folosite numai în corpul funcţiei respective, adică începând din momentul apelării funcţiei
respective şi până la revenirea din ea.
Astfel de variabile pot fi alocate pe stivă. În acest caz, lor li se alocă memorie pe stivă
la intrarea în funcţia în care sunt declarate şi îşi pierd alocarea respectivă la ieşirea din funcţie,
când se reface stiva la forma dinaintea apelului.
Stiva este o zonă de memorie cu o destinaţie specială. Ea are o structură de tip LIFO
(Last Input First Output). Într-o astfel de zonă de memorie se alocă pe rând memorie
variabilelor locale declarate în corpul funcţiilor. Eliberarea memoriei astfel alocate se face în
ordine inversă alocării ei, la terminarea execuţiei funcţiei respective.
Variabilele alocate în acest fel se numesc automatice. Ele se declară în corpul
funcţiilor şi au sintaxa obişnuită.
Se poate ca variabilele locale să nu fie alocate pe stivă, ci într-o zonă de memorie
destinată special în acest scop. O astfel de variabilă se spune că este statică.
O variabilă statică declarată în corpul unei funcţii are domeniul de valabilitate numai
corpul funcţiei respective, ca şi variabilele automatice. Spre deosebire de ele, variabila statică
nu se alocă pe stivă, ci într-o zonă de memorie destinată acestui scop.
Î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ă declarată în acest fel nu poate fi
declarată ca externă.
Menţionăm că tablourile de dimensiuni mari se recomandă a fi declarate statice,
deoarece dacă ele sunt automatice, pot să conducă la depăşirea stivei alocate programului.
Amintim că, implicit, dimensiunea stivei alocate unui program este de 4K octeţi.
Exemple:
1. Fişierul fis1.cpp este un fişier sursă care conţine două variabile globale i şi x o
variabilă statică y şi două funcţii main şi f, care şi ele conţin la rândul lor variabilele statice a
şi b.
/* variabile globale
int i; /* definitia variabilei i */
Limbajul de programare C 176
double x; /*definitia variabilelor x
*/
/* variabile statice */
static int y;
void main ()
{ ………………………
static char a; /* variabila statica
*/
int c; /* variabila automatica
*/
……………………….
/* se pot folosi variabilele i, x, y, a si c*/
……………………….
}
f(…)…
{ int p; /* variabila automatica
*/
static float b; /* variabila statica */
…………………..
/* se pot folosi variabilele i, x, y, p si b */
……………………….
}
Variabilele a şi c sunt locale funcţiei main şi ele nu pot fi folosite în funcţia f. La fel,
variabilele p şi b sunt locale în funcţia f şi nu pot fi folosite în funcţia main.
/* variabile statice */
static usingned t;
f1 (…)…
{ …………………
extern int i; /* declaratie externa pentru i */
extern double x; /* declaratie externa pentru x */
static int k;
……………….
/* se pot folosi variabilele i, x, k si t */
……………… }
177 Limbajul de programare C
f2(…)…
{ extern int i; /* declaratie externa pentru i */
static double s; /* variabila statica */
………………
/* se pot folosi variabilele i, s, si t */
………………… }
Se observă că variabila statică y, definită în fişierul fis1.cpp, nu poate fi utilizată în
fişierul fis2.cpp. De asemenea, variabila statică t nu poate fi folosită în fişierul fis1.cpp.
Variabila globală x nu poate fi folosită în funcţia f2, ea nefiind declarată ca şi externă.
Observaţii:
1. Variabilele globale constituie un mijloc simplu de interfaţă între funcţiile unui
program.
2. Funcţiile, ca şi variabile globale, pot fi utilizate în tot programul.
Întâi se alocă parametrul x într-un registru şi apoi se alocă a într-un alt registru.
Observaţie: Se recomandă alocarea în regiştri a variabilelor care au o utilizare mare,
ţinând seama însă, că numărul acestora este relativ mic.
6.9.4. Iniţializarea
Exemple:
int it[ ] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int matrice [4] [3] = {{-1, 0, 1}, {-1}, {0, 1}, {0, 0, 1}}.
Observaţii:
1. În cazul vectorului it nu s-a specificat la declarare dimensiunea acestuia deoarece
ea rezultă în urma iniţializării, fiind egală cu 9;
2. La iniţializarea elementelor tabloului matrice o parte din elemente pot rămâne
neiniţializate.
… …
… …
În figura 6.2 valoarea “2500” reprezintă adresa de memorie a valorii “5”, adică este un
pointer.
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 atribuirea: p = &x;
Fig. 6.3. Atribuirea numelui unui tablou unei variabile de tip pointer.
Dacă p este un pointer sunt corecte expresiile de forma: p+n şi p-n unde n este de tip
întreg.
183 Limbajul de programare C
Efect:
- expresia p+n măreşte valoarea lui p cu n∗t, unde t este numărul de octeţi necesari
pentru a memora o dată de un tip spre care pointează p;
- în mod analog, expresia p-n micşorează valoarea lui p cu n∗t.
Dacă a este un tablou cu elemente de tipul t, atunci a este un pointer, deci expresia
a+n este corectă şi va reprezenta un pointer spre elementul a[n] (adresa acestuia). Valoarea
acestui element se poate obţine din expresia ∗ (a+n).
În acest fel se pot înlocui indicii de tablou prin expresii cu pointeri.
Doi 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 t[i] respectiv t[j] ale
tabloului t, expresia p<q are sens şi ea este adevărată dacă i<j. De asemenea p! = q are
valoarea adevărată dacă i≠ j, etc.
Este permisă compararea pointerilor şi cu constanta simbolică NULL care se poate
interpreta ca “nici o adresă”. Astfel dacă expresia: p==NULL este adevărată înseamnă că p
nu conţine nici o adresă.
Doi pointeri care pointează spre elementele aceluiaşi tablou pot fi scăzuţi. Rezultatul
diferenţei a doi pointeri este definit astfel: fie a un tablou de un tip oarecare şi p, q doi
pointeri, p conţine adresa elementului a[i], iar q conţine adresa elementului a[i+n]. Atunci
diferenţa: q-p are valoarea egală cu n.
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:
Limbajul de programare C 184
void *malloc (usingned n);
unde n este numărul de octeţi ai zonei de memorie care se alocă.
În cazul în care n este prea mare, funcţia returnează pointerul NULL.
Funcţia calloc are prototipul:
void *calloc (usingned nrelem, usingned dimelem);
unde: - dimelem este dimensiunea în octeţi a unui element de dată;
- nrelem este numărul elementelor pentru are se alocă memorie.
Prin această funcţie se alocă nrelem*dimelem octeţi.
Ea returnează pointerul spre începutul zonei rezervate sau pointerul NULL în cazul în
care numărul octeţilor este prea mare (depăşeşte zona de memorie liberă afectată alocărilor
dinamice).
Elementele din zona de memorie alocată prin calloc au valoarea zero.
Funcţia free eliberează o zonă de memorie care în prealabil a fost alocată prin malloc
sau calloc. Prototipul ei este:
void free(void *p);
unde p este pointerul returnat de malloc sau calloc la alocare, deci este pointerul spre
începutul zonei care se eliberează.
Exemplu:
Funcţia memorează un şir de caracatere î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 *memoreaza (char *s)
/* memoreaza sirul de caractere spre care pointeaza s in zona
de memorie furnizata de malloc*/
{
if ((p = (char *) malloc (strlen(s)+1)) ! = NULL)
{ strcpy(p,s); return p;
}
else return NULL;
}
În exemplul anterior s-au utilizat două funcţii specifice şirurilor de caractere, strlen,
care returnează lungimea unui şir de caractere şi strcpy, care copiază un şir de caractere în
altul, funcţii definite în fişierul string.h.
185 Limbajul de programare C
6.10.5. Prelucrarea tablourilor unidimensionale folosind pointeri
S-a arătat că numele unui tablou este pointer constant care are ca valoare adresa
primului element al său. Pentru declaraţia:
tip a[…];
identificatorul a are ca valoare adresa elementului a[0] , adică &a[0].
Fie tip*p; atunci atribuirea de forma p = a; este corectă şi în urma acestei atribuiri, p
are valoare tot adresa lui a[0].
Dacă utilizăm ca parametru efectiv numele unui tablou unidimensional, atunci
parametrul formal corespunzător poate fi declarat fie ca un tablou unidimensional (cu
paranteze pătrate vide), fie printr-un pointer:
tip a [100]
…
… f(a);
…
Antetul lui f se defineşte prin unul din formatele:
… f(tip t[]) sau … f(tip*t).
Indiferent care dintre antete se alege, în corpul funcţiei f se pot folosi atât variabilele cu
indici: t[exp] cât şi expresii cu pointeri: *(t+exp) pentru a face acces la elementele tabloului
tab.
S-a arătat că în general, dacă tab este un tablou unidimensional de tipul tip, atunci a+n
este chiar adresa elementului a[n]. Deci:
x = a[n] sau x = *(a+n)
sunt atribuiri care au acelaşi effect (atribuire lui x valoarea elementului a[n]). Expresiile cu
pointeri pot fi folosite şi în stânga operatorului de atribuire, adică:
a[n] = x
şi *(a+n) = x
au acelaşi efect.
Din cele de mai sus rezultă că variabilele cu indici se pot echivala prin expresii cu
pointeri. Este posibil să procedăm şi invers, adică să înlocuim expresiile cu pointeri prin
variabile cu indici, deşi acest lucru nu este recomandabil.
Fie, de exemplu, declaraţia:
tip*p = (tip*)malloc(...);
Expresia *(p+n) este echivalentă cu expresia p[n].
Exemplu:
Programul următor calculează şi afişează suma elementelor întregi ale unui vector de
dimensiune n .
#include <stdio.h>
Limbajul de programare C 186
#include <conio.h>
void main()
{ int a[100],i,n;
printf("\nIntroduceti numarul de elemente al vectorului");
scanf("%d%",&n);
printf("\nIntroduceti elementele vectorului:");
for (i=0;i<n;i++)
{ printf("\na[%d]=",i);
scanf("%d",a+i); }
int s=0;
for (i=0;i<n;i++)
s=s+*(a+i);
printf("\nSuma elementelor vectorului a este: %d",s);
getch(); }
Se observă că atât la citirea elementelor cât şi la calculul sumei s-au utilizat expresii cu
pointeri în locul expresiilor cu indici .
Legătura dintre tablouri şi pointeri ne oferă o altă soluţie de prelucrare a unor mulţimi
de valori, nu prin intermediul unui tablou clasic, ci prin intermediul unui pointer. Varianta cu
pointer oferă avantajul alocării stricte a unui număr de componenete, exact câte are nevoie
programatorul.
Vom considera un exemplu în care vom renunţa la tablouri statice şi vom folosi o
variantă dinamică, bazată pe pointeri. Fie notele la laborator ale studenţilor unei grupe, note
stocate într-un vector declarat ca pointer. Se doreşte afişarea notelor studenţilor, determinarea
mediei grupei la laborator şi numărarea a câte note peste 8 există.
Datele se pot stoca într-un vector static, de genul int Note[30], unde s-a estimat
existenţa a maxim 30 de studenţi. Varianta dinamică constă în folosirea unui pointer căruia i se
rezervă o zonă exactă de memorie, în care să se stocheze acele valori, pointer declarat astfel:
int *Note. Folosind operaţiile cu pointeri prezentate anterior, se pot accesa valorile din zona de
memorie alocată.
Prezentăm, mai jos, programul respectiv :
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
int N, *Note; //se declară pointerul folosit la stocarea datelor
Numele unui tablou bidimensional, la fel ca numele unui tablou unidimensional, are ca
valoare adresa primului element al tabloului.
Cum *(tab+i) are ca valoare chiar tab[i], rezultă că tab[i][0] are aceeaşi valoare cu
*(*(tab+i)).
De asemenea, tab[i]+j are ca valoare adresa lui tab[i][j]. Deci tab[i][j] are aceeaşi
valoare ca şi expresia *(tab[i]+j), iar aceasta din urmă are aceeaşi valoare cu expresia
*(*(tab+i)+j).
Înseamnă că atribuirile *(*(tab+i)+j) = x şi tab[i][j] = x sunt echivalente.
Dacă tab este un tablou unidimensional declarat prin:
tip tab[...];
atunci tab are tipul tip* adică pointer spre tip.
Dacă tab este un tablou bidimensional declarat prin:
tip tab[m][n];
atunci tab are tipul tip (*)[n] adică pointer spre tip.
Menţionăm că în declaraţia de mai sus m şi n sunt expresii constante.
Să presupunem că tab este parametru la apelul funcţiei f:
... f(tab).
Nume[I]
Nume[0]
Nume[1]
….
Nume[99]
char *Nume[99];
Se observă faptul că se declară 100 de adrese şi nu 100 de şiruri de caractere, soluţia
aceasta oferind o economie de memorie. Sursa programului este prezentată mai jos:
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
#include <string.h>
int N;
char *Nume[99]; // lista cu adresele celor n nume, maxim 100
void Citire (void)
{ int I, J;
printf("\n Dati numarul de persoane");
scanf("%d",&N);
for(I=0; I<N; I++)
Nume[I] = (char *) malloc (N * sizeof(char));//alocarea memoriei fiecărui
nume
for (I=0; I<N ; I++)
{ printf("\n Dati numele persoanei %d ",I+1);
scanf("%s", Nume[I]);
for (J=0; J<=strlen(Nume[I]); J++)
if ((*(Nume[I]+J)>='a')&&(*(Nume[I]+J)<='z'))
*(Nume[I]+J) -= 32; //fiecare litera mica se transforma in litera mare
}}
Orice funcţie are asociată o adresă fixă de memorie, anume adresa de început a
funcţiei. Această adresă de început este stocată în numele funcţiei. Prin urmare, numele unei
funcţii este un pointer spre funcţia respectivă. El poate fi folosit ca parametru efectiv la
Limbajul de programare C 194
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.
Pointerii spre funcţii sunt pointeri speciali ce permit declarea parametrilor de tip
funcţie. Cu aceşti parametrii de tip pointer spre funcţie, putem transmite o funcţie ca
parametru la altă funcţie.
Forma generală a declaraţiei unui pointer la un tip de funcţie este :
tip (*pf) (listă parametrii formali) ;
unde tip este tipul valorii returnate de funcţie, pf va fi numele pointerului. Se remarcă prezenţa
parantezelor, fără ele declaraţia ar spune că pf este o o funcţie ce returnează un pointer. De
exemplu, declaraţia int (*p)(int x, int x) precizează că p este un pointer spre o funcţie de tip
int, care are doi parametrii.
Apelul unei funcţii prin intermediul unui pointer se face cu construcţia:
(*pf)(listă parametrii actuali);
sau var= (*pf)(listă parametrii actuali);
Să considerăm cazul în care doriţi să scrieţi o funcţie generală care să poată calcula
valoarea unei integrale dintr-o funcţie oarecare (integrabilă). De fapt, doriţi să rulaţi această
funcţie de calculul integralei cu diverse funcţii transmise de utilizator. Din păcate, în
momentul construirii funcţiei de calcul nu cunoaşteţi care este funcţia. Totuşi, acest lucru va fi
posibil folosind pointeri spre funcţii.
În primul rând, funcţiile pentru care se doreşte calculul integralei se vor putea declara
obişnuit, sub forma :
double f1(double x)
{ return x*x –1; }
sau
double f2(double x)
{ return sin (x *x); }
sau
double f3(double x)
{ return exp(x); }
Funcţia generală de calcul ( bazată pe o anumită metodă de integrare) va trebui să aibă
prototipul următor :
double Integrala( double A, double B, int N, double (*p)(double) );
unde A , B reprezintă capetele de integrare iar n numărul de puncte pentru diviziunea aleasă
(vom folosi o metodă de integrare bazată pe analiză numerică, folosind metoda trapezelor).
void main()
{ double ValInt,A,B;
ValInt=Integrala(1,2,100,f);
printf("\n valoarea integralei din f=x*x-1 este %lf",ValInt);
Limbajul de programare C 196
A=-3.14;
B=3.14;
ValInt=Integrala(A,B,100,f1);
printf("\n valoarea integralei din f=sin(x*x) este %lf",ValInt);
ValInt=Integrala(10,100,100,f2);
printf("\n valoarea integralei din f=exp(x) este %lf",ValInt);
getch(); }
197 Limbajul de programare C
6.10.8. Tratarea parametrilor din linia de comandă
În linia de comandă folosită la apelul execuţiei unui program se pot utiliza diferiţi
parametri. În cazul utilizării mediului de dezvoltare integrat Borland C, aceşti parametri se pot
defini utilizând submeniul Arguments al meniului Options. Se selectează meniul Options
folosind săgeţile sau tastând <ALT>O. Apoi se selectează submeniul Arguments cu ajutorul
săgeţilor sau tastând A. În acest moment se afişează o fereastră şi se vor tasta parametrii care
să fie prezenţi la lansarea programului. Parametrii se tastează unul după altul, separaţi prin
blancuri. După ultimul parametru se va acţiona tasta ENTER.
Aceşti parametri pot fi utilizaţi parametrii argc şi argv ai funcţiei principale.
Parametrul argc este de tip întreg şi indică numărul de parametri din linia de comandă
(sau diferiţi cu ajutorul submeniului Arguments). 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. În felul acesta, antetul funcţiei principale va fi:
main (int argc, char *argv[])
Exemplu:
Considerând că la lansarea programului prog s-au furnizat parametrii:
15 SEPTEMBRIE 2001
În acest caz argc = 4, iar tabloul argv conţine pointerii:
- argv[0] - pointeri spre numele programului (calea, numele şi extensia .EXE);
- argv[1] - pointeri spre „15”;
- argv[2] - pointeri spre „SEPTEMBRIE”;
- argv[3] - pointeri spre „2001”.
Observaţii:
1) Lansarea unui program se face cu prima instrucţiune a funcţiei principale. Parametrii
argc şi argv au deja în acest moment valorile indicate mai sus. Deci ei pot fi analizaţi chiar
începând cu prima instrucţiune a funcţiei principale.
2) În mod frecvent aceşti parametri reprezintă diferite opţiuni ale programului, date
calendaristice, nume de fişiere etc.
3) argv[0] este întotdeauna pointerul spre numele fişierului cu imaginea executabilă a
programului.
Exemple:
1. Programul următor afişează parametrii din linia de comandă.
#include <stdlib.h>
#include <string.h>
void main(int argc, char*argv [])
{ static int tabzi [] = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
static char *tpdl[] = {
“luna ilegala”, “ianuarie”, “februarie”, “martie”, “aprilie”, “mai”, “iunie”,
“iulie”, “august”, “septembrie”, “octombrie”, “noiembrie”, “decembrie” };
int zz, ll, aa, i;
if (argc! = 4) printf (“numar parametric eronat\n”);
else
if(strlen(argv[1])>2) printf(“peste 2 cifre la zi\n”) ;
199 Limbajul de programare C
else
if(strlen(argv[10])>10)
printf(„peste 10 caractere la denumire luna\n”);
else
if(strlen(argv[3])>4) printf(„peste 4 cifre la an\n”);
else { /*1 */
zz = atoi (argv[1]); /* se cauta luna in tpd1 */
for (ll = 1; ll<13; ll++)
if(strcmp (argv[2], tpd1[11])==0) break;
if(ll= = 13) /* nu s/a gasit luna */
printf(„denumirea lunii eronata\n”);
else { /* 2 */ /* se converteste anul */
aa = atoi (argv[3]);
if(aa<1600) printf(„anul eronat\n”);
else { /* 3 */
i = tabzi [11] + (ll = = 2&&(aa%4 = = 0&& aa%100!=0 aa%400= =0));
if (zz<1 zz>i) printf(„ziua eronata\n”);
else /* data calendaristica corecta */
printf(„%02d/%02d/%d\n”, zz, ll, aa);
} /* sfarsit else 3*/
} /* sfarsit else 2*/
} /* sfarsit else 1*/
} /* sfarsit main*/
În acest program s-a utilizat funcţia atoi care realizează o conversie a unui şir de
caractere într-un întreg. Funcţia se găseşte în fişierul antet stdlib.h.
#include <string.h>
void main()
{
int const i=10; int const j;
double const x = 1.23456789; double const pi;
const n=12; int const k; const 1;
char *t=”xyz”; char *const s=”sir”; char const *s1 = “abc”;
const s2 = “ABCDE”; char *p; int *q;
q = &j; *q = 1234;
201 Limbajul de programare C
*(int *) &k=3;
*(int *) &1 = -3;
printf(“dimensiunile lui I,j,n,1\n”);
printf(“%d %d %d %d\n”, sizeof(i), sizeof(j), sizeof(n), sizeof(1));
printf(“dimensiunile lui x,s,s1,s2\n”);
printf(“%d %d %d %d\n”, sizeof(x), strlen(s), strlen(s1), strlen(s1),
strlen(s2));
printf(“valorile lui i,n,s,s1,s2\n”);
printf(“i = %d n=%d s=%s s1=%s s2=s%\n”, i,n,s,s1,s2);
printf(“valorile lui j,k,1\n”);
printf(“j=%d k=%d l=%d\n”, j,k,l);
*(double *) &pi=3.14159;
printf(“valorile lui x,pi\n”);
printf(“x=%.10f pi=%.10f\n”, x,pi);
/* modificari folosind pointeri */
*(int *)&i = 10000;
q=&l; *q=7;
printf(“valorile modificate ale lui i si l\n”);
printf(“i=%d l=%d\n”, i, l);
*(double *) &pi = 3.14159265;
printf(“valorea modificata a lui pi\n”);
printf(“%.10f\n”, pi);
*s = ‘1’; *(s+1) = ‘2’;
printf(“valorea modificata a zonei spre care pointeaza s\n”);
printf(“s=%s\n”, s);
p = s1; *p=’1’;
printf(“valorea modificata a zonei spre care pointeaza s1\n”);
printf(“s=%s\n”, s1);
}
Rezultatele programului:
dimensiunile lui i,j,n,1
2 2 2 2
dimensiunile lui x,s,s1,s2
8 3 3 5
valorile lui I,n,s,s1,s2
i = 10 n = 12 s = sir s1 = abc s2 = ABCDE
Valorile lui j,k,l
j = 1234 k=3 l=3
Limbajul de programare C 202
Valorile lui x, pi
x = 1.2345678900 pi=3.1415900000
Valorile modificate ale lui I si l
i = 10000 l=7
valoarea modificata a lui pi
3.1415926000
valoarea modificata a zonei spre care pointeaza s
s = 12r
valoarea modificata a zonei spre care pointeaza s1
s1 = 1bc
Observaţii:
1) Declaraţia const permite protejarea valorii atribuite unui nume faţă de eventualele
încercări ulterioare de a modifica accidental aceste valori prin simple atribuiri.
2) În cazul în care programatorul doreşte să facă o astfel de modificare, ea se poate
realiza, dar nu direct, ci doar indirect, folosind pointerii.
De exemplu, dacă dorim să modificăm valoarea constantei i din exemplu 1 de la 10 la
100, putem realiza acest lucru prin instrucţiunea următoare:
*(int*)&i = 100
Aceasta este posibil deoarece unui nume declarat prin const i se alocă memorie.
Expresia de mai sus este echivalentă cu secvenţa:
int *p;
................
p = &i;
*p = 100;
Într-adevăr, &i reprezintă adresa zonei alocate numelui i. Atunci (int *)&i converteşte
această adresă spre un pointer spre întregi, deci ea are aceeaşi valoare ca şi p după
instrucţiunea p = &i; Prin urmare, instrucţiunea *p = 100; devine echivalentă cu
*(int*)&i = 100;
Modificatorul const se foloseşte frecvent la declaraţia parametrilor formali de tip
pointer. O astfel de declaraţie are formatul:
Funcţia strcpy returnează adresa din dest, adică adresa zonei în care s-a copiat şirul.
3. Funcţia strcat din biblioteca standard a limbajului C are prototipul:
char*strcat(char*a, const char*b);
Şirul spre care pointează b se concatenează la sfârşitul şirului spre care pointează a.
Şirul spre care pointează nu poate fi modificat de către funcţia strcat şi din această cauză
parametrul b este declarat cu modificatorul const.
Funcţia returnează pointerul spre şirul rezultat, deci chiar valoarea parametrului a.
În anumite situaţii practice se lucrează cu seturi mari de date. Aceste date pot să fie
toate de acelaşi tip sau să fie de tipuri diferite. Dacă datele sunt de acelaşi tip ele se pot grupa
în structuri de tip tablou. Dacă sunt de tip diferit, spunem despre grupa respectivă de date că
formează o structură.
Observaţie:
De obicei, numele asignat unui tip se scrie cu litere mari. 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:
1. 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 x,y,a [100];
este identică cu declaraţia: int x,y,a [100];
În mod analog:
REAL p,q,r;
este identică cu declaraţia: float p,q,r;
2. typedef struct dat_calend { int zi;
char luna [11];
int an;
} DC;
Prin această declaraţie se atribuie denumirea DC tipului structurat data_calend. În
continuare putem declara date de tip DC:
DC data_angajarii, data_nasterii;
DC data_crt = {20, “septembrie”, 1991};
3. Programul următor citeşte numere complexe aflate în fişierul standard stdin şi le
rescrie împreună cu modulul lor. Un număr complex se introduce printr-o pereche de
numere flotante, primul reprezentând partea reală, iar cel de al doilea partea imaginară.
typedef struct { double real;
double imaginar;
} COMPLEX;
double modul (COMPLEX *);
void main ()
/*citeste numere complexe si le rescrie impreuna cu modulul lor*/
{ COMPLEX z;
while (scanf(“%lf %lf”, &z.real, &z.imaginar)= = 2)
printf (“%lf %lf, modul = %lf\n”,z.real, z.imaginar, modul(&z)); }
#include <math.h>
double modul (COMPLEX *x)
/*returneaza modulul numarului complex spre care pointeaza x*/
{ return sqrt (x->real* x->real+x->imaginar* x->imaginar); }
209 Limbajul de programare C
void citire_date()
{ printf("\nNumar candidati:");
scanf("%d",&n);
printf("\nNumar locuri la profilul a:");
scanf("%d",&na);
printf("\nNumar locuri la profilul b");
scanf("%d",&nb);
printf("\nNumar locuri la profilul c");
211 Limbajul de programare C
scanf("%d",&nc);
float x;
int i,j;
for (i=0;i<n;i++)
{ printf("\nNumele:");
scanf("%s",a[i].nume);
printf("\nPrenumele:");
scanf("%s",a[i].pren);
printf("\nMedia :");
scanf("%f",&x);
a[i].med=x;
for (j=0;j<3;j++)
{ printf("\nOptiunea %d ",j+1);
fflush(stdin);
scanf("%c",&a[i].opt[j]); }
a[i].repartizat=' ';
}
}
void ordonare_medii()
{ int i,gata;
Candidat temp;
do
{ gata=1;
for (i=0;i<n-1;i++)
if (a[i].med<a[i+1].med)
{ temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
gata=0; }
}
while (gata==0);
}
void afisare_candidati()
{ int i;
printf("\nLista candidatilor este\n");
printf("\n*************************************");
for (i=0;i<n;i++)
Limbajul de programare C 212
printf("\n%20s %20s %.2f",a[i].nume,a[i].pren,a[i].med);
}
void repartizare_optiuni()
{ int i,j;
for (i=0;i<n;i++)
for (j=0;j<3;j++)
if (a[i].repartizat==' ')
switch (a[i].opt[j])
{ case 'a':if (na>0)
{ a[i].repartizat='a';
na--; }
break;
case 'b':if (nb>0)
{ a[i].repartizat='b';
nb--; }
break;
case 'c':if (nc>0)
{ a[i].repartizat='c';
nc--; }
break
}
}
void afisare_rezultate()
{ int i,j;
printf("\nLista cu candidatii admisi este ");
printf("\n**********************************");
for (i=0;i<n;i++)
if (a[i].repartizat!=' ')
printf("\n%20s %20s media %.2f profilul %c",a[i].nume,a[i].pren,a[i].med,
a[i].repartizat);
printf("\nLista cu candidatii respinsi este ");
printf("\n**********************************");
for (i=0;i<n;i++)
if (a[i].repartizat==' ')
printf("\n%20s %20s media %.2f ",a[i].nume,a[i].pren,a[i].med);
}
void main()
213 Limbajul de programare C
{ citire_date();
afisare_candidati();
ordonare_medii();
repartizare_optiuni();
afisare_rezultate();
getch();
}
6.11.4. Uniuni
#define Pi 3.1415927
#define Cerc 1
#define Patrat 2
#define Dreptunghi 3
#define Triunghi 4
typedef struct { int tip_figura;
union{ float raza;
float latura;
float lat_drept[2];
float lat_tr[3]; } figura;
} FIGURA;
void main()
{ float aria,p;
char tip;
int i;
FIGURA fig; //se citeste caracterul care defineste tipul
figurii
printf("\nIntroduceti tipul figurii:");
scanf("%c",&tip);
switch (tip) { case 'C': //cerc
printf("\nIntroduceti raza cercului:");
scanf("%f",&fig.figura.raza);
fig.tip_figura=Cerc;
break;
case 'P': //patrat
printf("\nIntroduceti latura patratului");
scanf("%f",&fig.figura.latura);
fig.tip_figura=Patrat;
break;
case 'D': //dreptunghi
printf("\nIntroduceti laturile dreptunghiului");
scanf("%f%f",&fig.figura.lat_drept[0],
&fig.figura.lat_drept[1]);
fig.tip_figura=Dreptunghi;
break;
215 Limbajul de programare C
case 'T':
//triunghi
printf("\nIntroduceti laturile triunghiului");
for (i=0;i<3;i++)
scanf("%f",&fig.figura.lat_tr[i]);
fig.tip_figura=Triunghi;
break;
default: printf("\nEroare");
break;
}
switch (fig.tip_figura)
{ case Cerc: printf("\nAria cercului este %f",fig.figura.raza*fig.figura.raza*Pi );
break;
case Patrat:printf("\nAria patratului este %f"
,fig.figura.latura*fig.figura.latura);
break;
case Dreptunghi: printf("\nAria dreptunghiului este %f",
fig.figura.lat_drept[0]*fig.figura.lat_drept[1]);
break;
case Triunghi: for(p=0,i=0;i<3;i++)p+=fig.figura.lat_tr[i];
p=p/2;
for(i=0;i<3;i++)
if (p<=fig.figura.lat_tr[i])
{printf("\nValorile nu pot fi laturile unui triunghi");
return; }
for (aria=1,i=0;i<3;i++) aria*=(p-
fig.figura.lat_tr[i]);
aria=sqrt(p*aria);
printf("\nAria triunghiului este %f",aria);
break;
}
}
x
y
z
6.11.6. Enumerări
Pointerii sunt adrese de variabile de orice tip. Prin urmare, un pointer poate fi declarat
ca un pointer spre tipul structură.
Un pointer către o structură se declară similar cu pointerii către variabile simple.
Dacă TipStruct este un tip de date de tip structură definit explicit cu ajutorul construcţiei
typedef, declararea unui pointer spre structura aceea se face astfel:
TipStruct *P;
6.12. Fişiere
Orice fişier înainte de a fi prelucrat trebuie deschis. Deschiderea unui fişier existent se
realizează prin intermediul funcţiei open. La revenirea din ea se returnează aşa numitul
descriptor de fişier. Aceasta este un număr întreg. El identifică în continuare fişierul respectiv
în toate operaţiile realizate asupra lui.
În forma cea mai simplă, funcţia open se apelează printr-o expresie de atribuire.
Prototipul acestei funcţii este:
int open (const char*cale, int acces);
unde cale este pointer spre un şir de caractere care defineşte calea spre fişierul care se
deschide iar acces o variabilă de tip întreg care poate lua una din valorile următoare:
Exemple:
1) char nfis[ ] = “fis1.dat”;
int df;
…
df = open(nfis, O_RDONLY);
…
Prin apelul de mai sus se deschide în citire fişierul fis1.dat din directorul curent.
Descriptorul de fişier returnat de funcţia open se atribuie variabilei df.
2) char fis = “A:\\JOC\\BIO.C”;
int d;
...
d = open (fis, O_RDWR);
...
Prin acest apel se deschide în citire/scriere fişierul BIO.C din directorul JOC aflat pe
discul flexibil încărcat pe unitatea A.
3) int d;
d = open (“c:\\tc\\include\\text.h”, O_APPEND)
Se deschide în adăugare fişierul text.h din subdirectorul include al directorului tc de pe
discul C.
Observaţie:
La compilare şirul de caractere utilizat ca parametru se va păstra într-o zonă de date şi
el este înlocuit prin adresa zonei în care s-a memorat şirul respectiv.
Pentru a crea un fişier nou se va folosi funcţia creat în locul funcţiei open. Ea are
prototipul:
int crea (const char*cale, int mod);
unde cale este un parametru care se defineşte ca şi în cazul funcţiei open iar mod este un
întreg care poate fi definit prin constantele simbolice de mai jos:
S_IREAD proprietarul poate citi fişierul;
S_IWRITE proprietarul poate scrie în fişier;
Limbajul de programare C 224
S_IEXEC proprietarul poate executa programul conţinut în fişier.
Aceşti indicatori pot fi combinaţi folosind caracterul ‚’. De exemplu, pentru
citire/scriere se va folosi:
S_IREAD S_IWRITE proprietarul poate citi/scrie fişierul.
Observaţii:
1) Funcţia creat returnează descriptorul de fişier sau –1 în caz de eroare.
2) Utilizarea acestei funcţii implică includerea fişierelor io.h şi stat.h.
3) Funcţia creat poate fi utilizată şi pentru a deschide un fişier existent. În acest caz
vechiul fişier se şterge şi în locul lui se va crea unul nou cu acelaşi nume.
Operaţia de citire a unei înregistrări dintr-un fişier deschis în prealabil cu funcţia open
se realizează cu ajutorul funcţiei read. Ea returnează numărul octeţilor citiţi din fişier.
Prototipul acestei funcţii este
int read(int df, void*buf, unsigned lung);
unde:
- df este descriptorul de fişier a cărui valoare a fost definită la apelul funcţiei open
pentru fişierul respectiv;
- buf este pointerul spre zona de memorie în care se recepţionează înregistrarea care se
citeşte;
- lung este lungimea în octeţi a înregistrării citite.
La eroare, funcţia read returnează valoarea –1. La fiecare apel al funcţiei read se
citeşte înregistrarea curentă. Astfel, la primul apel se citeşte prima înregistrare din fişier, la al
doilea apel a doua înregistrare şi aşa mai departe. Ordinea înregistrărilor este cea definită la
crearea fişierului şi eventual la adăugarea de înregistrări după crearea lui. La un apel al
funcţiei read se citesc cel mult lung octeţi, înregistrarea având lungimea definită la scrierea ei
în fişier. La sfârşit de fişier nu se citeşte nimic şi deci funcţia read returnează valoarea zero.
Dacă lung = 1, atunci se citeşte un singur octet. De obicei nu este eficient să se
citească câte un octet dintr-un fişier, deoarece apelurile multiple ale funcţiei read pot conduce
la un consum de timp apreciabil. De aceea se folosesc frecvent înregistrări de 512 octeţi sau
mai mari.
Funcţia read poate fi folosită pentru a citi de la intrarea standard (de la terminalul de
la care s-a lansat programul). În acest caz, descriptorul de fişier este 0. Programatorul nu
trebuie să deschidă acest fişier deoarece el este deschis automat la lansarea în execuţie a
programului.
Utilizarea funcţiei read presupune includerea fişierului io.h.
Aşa cum s-a arătat mai sus, operaţiile de citire sau scriere într-un fişier se execută
secvenţial, adică la fiecare apel al funcţiei read sau write se citeşte înregistrarea curentă,
respectiv se scrie înregistrarea în poziţia curentă de pe suportul fişierului. Acest mod de acces
la fişier se numeşte secvenţial şi el este util când dorim să prelucrăm fiecare înregistrare a
fişierului, una după alta. În practică apar însă şi restricţii în care dorim să scriem şi să citim
înregistrări într-o ordine aleatoare. În acest caz se spune că accesul la fişier este aleator. Pentru
a se realiza un acces aleator este nevoie să ne putem poziţiona oriunde în fişierul respectiv. O
astfel de poziţionare este posibilă pe suporturile magnetice de tip disc şi se realizează folosind
funcţia lseek. Ea are prototipul:
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
citire/scriere 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 citire/scriere;
2 deplasamentul se consideră de la sfârşitul fişierului.
Observaţii:
1) Prin apelul lui lseek nu se realizează nici un fel de transfer de informaţie, ci numai
poziţionarea în fişier. Operaţia următoare realizată prin apelul funcţiei read sau write se va
realiza din această poziţie a capului de citire/scriere.
Limbajul de programare C 226
2) Funcţia returnează poziţia capului de citire/scriere faţă de începutul fişierului, în
număr de octeţi. În caz de eroare se returnează 1L.
3) Utilizarea funcţiei lseek presupune includerea fişierului io.h.
4) Apelul lseek(df, 01, 2) permite o poziţionare la sfârşitul fişierului al cărui descriptor
este df. În mod analog, apelul lseek(df,01,0) permite o poziţionare la început de fişier.
După terminarea 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. Această limită este dependentă de sistemul de operare şi ea variază, de obicei, în
intervalul 15-25.
Observaţie:
Fişierele corespunzătoare intrărilor şi ieşirilor standard nu se închid de către
programator.
Funcţia close are prototipul:
int close(int df);
unde df este descriptorul fişierului care se închide. La o închidere normală, funcţia returnează
valoarea 0. În caz de incident se returnează valoarea –1. Utilizarea funcţiei close implică
includerea fişierului io.h.
Exemple:
1. Programul următor copiază intrarea standard la ieşirea standard folosind o zonă
tampon de 70 de caractere.
#define LZT 70
#include <io.h>
void main() /* copiaza intrarea standard la
ieşirea standard */
{
char zt[LZT];
int 1;
while(1 = read (0, zt, LZT)) > 0) write(1, zt, 1);
}
#include <stdio.h>
#include <stat.h>
#include <fcnt1.h>
#include <io.h>
void main () /* citeste un sir de numere flotante si creează
fis1.dat cu numerele de ordin impar si fis2.dat cu
numerele de ordin par; în final se listeaza fisierele */
{ union unr
{ float nr;
char tnr [sizeof(float)]; };
union unr nrcit;
int df1, df2;
int i, j;
/*se deschid fisierele in creare cu acces in citire/scriere*/
if ((df1 = creat(“fis1.dat”, S_IWRITES_IREAD)) == -1)
{ printf(“nu se poate deschide fisierul fis1.dat in creare \n”);
exit(1); }
if ((df2 = creat(“fis2.dat”, S_IWRITES_IREAD)) == -1)
{ printf(“nu se poate deschide fisierul fis2.dat in creare \n”);
exit(1); }
/* se citeste sirul de numere si se pastreaza in cele doua fisiere */
j = 1;
while ((i = scanf(“%f”, &nrcit.nr)) == 1)
{ if (j%2 == 1)
{ /*se scrie in fisierul fis1.dat deoarece j este impar*/
if (( write (df1, nrcit.tnr, sizeof(float)))!= sizeof (float))
{ printf(“eroare la scriere in fisierul fis1.dat\n”);
exit(1); }
}
else /* se scrie in fisierul fis2.dat fiindca j este par */
if ((write(df2, nrcit.tnr, sizeof(float)))!= sizeof(float))
{ printf(“eroare la scriere in fisierul fis2.dat\n”);
exit(1); }
Limbajul de programare C 228
j++;
} /* sfarsit while */
if (close(df1) < 0) /* se inchid fisierele */
{ printf(“eroare la inchiderea fisierului fis1.dat\n”);
exit(1); }
if ((df1 = open (“fis1.dat”, O_RDONLY)) == -1)
{ printf(“nu se poate deschide fisierul fis1.dat in citire\n”);
exit(1); } /* se deschide fisierul fis1.dat in citire */
j = 1; /* se listeaza fisierul fis1.dat */
while((i = read(df1, nrcit.tnr, sizeof(float))) > 0)
{ printf(“%6d %g\n”, j, nrcit.nr);
j + = 2; }
if (i < 0)
{ printf(“eroare la citire din fis1.dat\n”);
exit(1); }
close(df1);
if ((df2 = open(“fis2.dat”, O_RDONLY) == -1)
{ printf(“nu se poate deschide fis2.dat in citire\n”);
exit(2); } /* se deschide fisierul fis2.dat */
printf(“n\n\n”); /* se listeaza fisierul fis2.dat */
j = 2;
while((i = read (df2, nrcit.tnr, sizeif (float))) > 0)
{ printf(“%6d %g\n”, j, nrcit.nr);
j + = 2; }
if (i < 0)
{ printf(“eroare la citire din fisierul fis2.dat\n”);
exit(1); }
close(df2);
}
Observaţie:
nrcit este o reuniune căreia i se alocă 4 octeţi, adică o dimensiune capabilă să păstreze
un număr flotant în simplă precizie. Zona respectivă este organizată şi ca un tablou de tip
char, deoarece funcţiile read şi write necesită o zonă tampon de acest tip la majoritatea
implementărilor limbajului C.
La acest nivel se utilizează funcţia fopen 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:
FILE *fopen(const char cale, const charmod);
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 se defineşte în felul următor:
„r” – deschidere în citire(read);
Fişierele pot fi scrise şi citite caracter cu caracter, folosind două funcţii simple şi
anume putc pentru scriere şi 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. În particular, pf poate fi unul din
pointerii:
Limbajul de programare C 230
stdout ieşire standard;
stderr ieşire pe terminal în caz de eroare;
stdaux comunicaţie serială;
stdprn ieşire paralelă la imprimantă.
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. Ea returnează codul ASCII a caracterului citit sau EOF la sfârşit de
fişier sau eroare. În particular, pf poate fi pointerul stdin (intrare de la tastatură).
După terminarea prelucrării unui fişier, aesta urmează a fi închis. În acest caz se
utilizează funcţia fclose. Ea 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:
1. Programul copiază intrarea standard la ieşirea standard stdout, folosind funcţiile
getc şi putc.
#include <stdio.h>
void main( ) /*copiaza intrarea stdin la ieşirea stdout */
{ int c;
while((c = getc(stdin)) ! = EOF) putc(c, stdout);
}
Observaţie:
Macrourile getchar şi putchar sunt definite în fişierul stdio.h astfel:
#define getchar ( ) .getc(stdin)
#define putchar(c) putc(c, stdout)
2. Programul următor copiază intrarea standard la imprimantă.
#include <stdio.h>
void main ( ) /*copiază caracterele din stdin la imprimanta */
{ int c;
while ((c = getc(stdin)) != EOF) putc(c, stdprn);
}
231 Limbajul de programare C
3. 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(argc, argv) /* listeaza la iesirea stdout un fisier a carui cale
este argumentul din linia de comanda */
int argc;
char *argv [ ];
{ FILE *pf;
int c;
if(argc == 1) /* nu exista argument in linia de comanda */
pf = stdin;
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); }
Biblioteca standard a limbajului C conţine funcţiile fgets şi fputs care permit citirea
respective scrierea într-un fişier ale cărui înregistrări sunt şirurile 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 iar 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. Dacă şirul se
termină cu ”\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 ”\n”. 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 tipul FILE care defineşte fişierul în care se scrie.
Funcţia fputs returnează codul ASCII al utilimului 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 cu ”\0”.
În mod analog, pentru a scrie la ieşirea standard 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.
Fiş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);
unsigned fwrite (const void * ptr, unsigned dim, unsigned nrart, FILE * pf);
unde:
- ptr este pointerul spre zona tampon ce conţine articole citite (înregistrarea citită);
- dim este un întreg ce reprezintă lungimea unui articol;
- nrart este un întreg ce reprezintă numărul de articole ce se transferă;
- pf este un pointer spre tipul FILE care defineşte fişierul din care se face citirea.
Ambele funcţii returnează numărul de articole transferate sau –l în caz de
eroare.
Exemple:
1. Programul citeşte de la intrarea stdin datele ale căror formate sunt definite mai jos şi le
scrie în fişierul misc.dat din directorul curent. Formatul datelor de intrare este următorul
Limbajul de programare C 234
tip denumire um cod pret cantitate
I REZISTENTA010KO 123456789 1.5 10000.0
#include <stdio.h>
#define MAX 50
typedef struct { char tip [2];
char den [MAX +1];
int val;
char unit[3];
long unit [3]
float pret;
float char; }
ARTICOL;
union {ARTICOL a[6]; /* 6 articole in zona tampon */
} buf;
int cit (ARTICOL *str)
// citeste datele de la intrarea standard si le scrie in structura spre care pointeaza str
{ int c, nr;
float x,y;
Observaţie:
În TURBO C se pot face atribuiri de structuri, spre deosebire de unele versiuni ale
limbajului C care nu admit acest lucru. Deci atribuirea buf.a[i] = a; utilizată anterior este
corectă.
2. Programul următor listează articolele fişierului misc.dat, creat prin programul anterior,
pentru care tip = I.
#include<stdio.h>
#define MAX 50
typedef struct { char tip[2];
char den[MAX + 1];
int val;
char unit[3];
long cod;
float pret;
float cant;
}
ARTICOL;
union { ARTICOL a[6];
char zt[6*sizeof (ARTICOL)]; } buf;
void main () /* citeste fisierul misc.dat */
{ FILE *pf;
int i,n; ARTICOL a;
if ((pf = fopen(“misc.dat”, “rb”)) == NULL)
{ printf(“nu se poate deschide fisierul misc.dat in citire\n”);
exit(1); }
Limbajul de programare C 236
printf(“%60s\n\n\n”, “INTRARI\n\n”); /* se citeste o inregistrare */
while ((n = fread (buf.zt, sizeof(ARTICOL), 6, pf )) > 0)
/* se listeaza articolele de tip I dintr-o inregistrare */
for (i = 0; i < n; i++)
{ a = buf.a[i];
if (a.tip[0] == ‘I’)
printf (“%-50s %03d%s %91d %.2f %.2f\n”,
a.den, a.val, a.unit, a.cod, a.pret, a.cant);
}
fclose(pf);
}
6.13.1. Introducere
Scrierea unui program complex, care să respecte regulile programării structurate,
presupune împărţirea programului în module. Folosirea modulelor pentru structurarea
programului oferă mai multe avantaje, motiv pentru care este acceptată ca o metodă de lucru
generală în scrierea programelor. În primul rând, dezvoltarea aplicaţiei se poate face în echipă,
fiecare membru al echipei realizând anumite module ale aplicaţiei. În al doilea rând, aplicaţia
poate fi depanată şi adaptată mai uşor. De asemenea, codul din aceste module poate fi refolosit
şi în alte aplicaţii.
Modulele sunt, de obicei, funcţii C, având diverse scopuri. Funcţiile C sunt folosite
pentru a împărţi anumite părţi ale programului şi pentru construirea de funcţii de calcul. Există
două metode de lucru, pentru construirea programului. Prima metodă, constă în scrierea
acestor funcţii în unul sau mai multe fişiere sursă C şi încluderea lor în fişierul principal ce
conţine funcţia main(), folosind directiva de compilare #include. A doua metodă, constă în
folosirea proiectelor C pentru gruparea fişierelor sursă şi a bibliotecilor folosite în program şi
realizarea aplicaţiei finale prin intermediul proiectului.
237 Limbajul de programare C
Fiecare metodă are avantaje şi dezavantaje. Prima se poate folosi pentru aplicaţii
simple, nu foarte mari. În schimb, a doua metodă este recomandată pentru aplicaţii ce necesită
cod mult (şi timp de dezvoltare mare). Le vom prezenta în detaliu, în continuare:
Această metodă este folosită, de obicei, în faza de învăţarea a unui limbaj sau pentru
realizarea unei aplicaţii simple. Programatorul îşi scrie codul funcţiilor în unul sau mai multe
fişiere sursă. Dacă programul nu are foarte multe funcţii, aceste funcţii se depun într-un singur
fişier sursă, fişier ce va conţine şi funcţia main(). Eventual, dacă programatorul defineşte
tipuri de date, poate construi un fişier cu extensia h (numit header) în care să depună
definiţiile de tipuri şi prototipurile funcţiilor din fişierul sursă, header ce se include şi el în
fişierul sursă. În schimb, dacă programul are multe funcţii, cu funcţiuni diferite, ele se pot
scrie în unul sau mai multe fişiere sursă C, pe care să le includă în fişierul cu funcţia main().
Vom considera un exemplu practic pentru a putea surprinde elementele de bază ale
acestei metode. Să presupunem că dorim realizarea unui program care să permită operarea cu
structuri de date de tip vector. Pentru aceasta, fişierul sursă va trebui să conţină funcţii pentru
citire, afişare, ordonare şi pentru calculul sumei elementelor vectorului. Soluţia va consta în
realizarea unui singur fişier sursă, care să conţină rutinele necesare operării cu strucura de tip
vector şi funcţia main().
Programul va arăta astfel :
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
// Se scriu prototipurile funcţiilor ce se folosesc în program, eventual la fiecare
// funcţie se scrie sub formă de comentariu la ce foloseşte
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
void Citire(int X[],int &N)
{ int i;
printf("\nDati numarul de elemente ale vectorului:");
scanf("%d",&N);
for (i=0;i<N;i++)
{ printf("X[%d]=",i);
scanf("%d",&X[i]);
}
Limbajul de programare C 238
}
void Afisare(int X[],int N)
{ int i;
printf("\nVectorul ");
for(i=0;i<N;i++)
printf("\n%d",X[i]);
}
void Ordonare(int X[],int N)
{ int i,k,temp;
do
{ k=0;
for(i=0;i<N-1;i++)
if(X[i]>X[i+1])
{ temp=X[i];
X[i]=X[i+1];
X[i+1]=temp;
k=1;
}
}
while (k!=0);
}
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
int Minim(int X[],int N);
int NumararePoz(int X[],int N);
int Cautare(int X[],int N,int Y);
void Citire(int X[],int &N)
{ int i;
printf("\nDati numarul de elemente ale vectorului:");
scanf("%d",&N);
for (i=0;i<N;i++)
{ printf("X[%d]=",i);
scanf("%d",&X[i]); }
Limbajul de programare C 240
}
void Afisare(int X[],int N)
{ int i;
printf("\nVectorul ");
for(i=0;i<N;i++)
printf("\n%d",X[i]);
}
void Ordonare(int X[],int N)
{ int i,k,temp;
do
{ k=0;
for(i=0;i<N-1;i++)
if(X[i]>X[i+1])
{ temp=X[i];
X[i]=X[i+1];
X[i+1]=temp;
k=1;
}
}
while (k!=0);
}
#include <stdio.h>
#include <conio.h>
#include "funcţii.cpp" // se include fişierul sursă, existent în directorul curent
void main()
{ int Optiune,Y;
int X[100],N;
do
{ clrscr();
printf("\n 1-Citire");
printf("\n 2-Afisare");
printf("\n 3-Calcul suma");
printf("\n 4-Ordonare");
printf("\n 5-Minim elemente");
printf("\n 6-Numarare elemente pozitive");
printf("\n 7-Cautare pozitie element");
printf("\n 8-Iesire");
printf("\nAlegeti optiunea ");
scanf("%d",&Optiune);
switch(Optiune)
{
case 1 :Citire(X,N);
Limbajul de programare C 242
break;
case 2 :Afisare(X,N);
break;
case 3 :printf("\nSuma elementelor este %ld",Suma(X,N));
break;
case 4 :Ordonare(X,N);
break;
case 5 :printf("\nMinimul elementelor este %d",Minim(X,N));
break;
case 6 :printf("\nNumarul elementelor pozitive este %d",
NumararePoz(X,N));
break;
case 7 :printf("\Dati elementul pe care il cautati");scanf("%d",&Y);
printf("\nPozitia elementului este %d",Cautare(X,N,Y));
break;
}
getch();
}
while ( Optiune!=8);
}
Această variantă este mult mai flexibilă decât varianta cu un singur fişier sursă. De
exemplu, puteţi refolosi fişierul sursă şi în alte aplicaţii, doar prin includerea lui în acele
aplicaţii .
Metoda prezentată anterior are câteva dezavantaje. În primul rând, dacă se fac
modificări într-una din funcţii, la recompilarea programului se vor recompila şi celelalte (în
mod inutil ). Acest lucru are ca efect (mai ales pentru programe mari) un consum de timp
mare. În al doilea rând, dacă se doreşte reutilizarea codului, fără a oferi şi codul sursă, prima
variantă nu permite acest lucru. De asemenea, utilizarea bibliotecilor obj sau lib, nu este
permisă în aceea variantă. Aceste dezavantaje dispar dacă programul este format din mai multe
fişiere sursă, legate între ele prin intermediul proiectului. Această metodă a devenit astăzi o
metodă standard de lucru. Cea mai mare parte a mediile de programare, inclusiv cele vizuale
de ultimă generaţie, folosesc noţiunea de proiect pentru a pune la un loc entităţile programului.
Ce este de fapt un proiect C? Proiectul este de fapt un fişier cu extensia PRJ, fişier ce
conţine informaţii despre părţile programului. Acest proiect se construieşte şi gestionează prin
intermediul comenzilor din meniul Project al mediului de programare (toate variantele
Borland conţin acest meniu).
243 Limbajul de programare C
Vom prezenta mai departe modul de lucru cu proiecte. Realizarea unui proiect,
presupune parcurgerea mai multor etape:
• scrierea funcţiilor C în mai multe fişiere sursă, de obicei grupate după funcţiuni şi
fără a depăşi un număr de aproximativ 15 funcţii.
• realizarea fişierelor header, fişiere cu extensia h, ce conţin definiţiile de tipuri,
declaraţiile de variabile şi prototipurile funcţiilor dintr-un fişier sursă. Pentru
fiecare fişier sursă ( mai puţin cel principal) se va realiza un fişier header. Acest
fişier header se va include în alte fişiere sursă ce folosesc funcţii care nu sunt
definite acolo (folosind directiva #include). De exemplu, în fişierul principal va
trebui să includem fişierul header, dacă vom folosi funcţii de operare cu vectori.
Acest lucru este necesar compilatorului, acesta fiind avertizat despre existenţa
funcţiilor ( prin urmare, nu se va mai semnala o eroare legată de neexistenţa acelor
funcţii).
• crearea proiectului, folosind din meniul Compile, comanda Open, urmată de
numele proiectului. Atenţie, numele proiectului va deveni automat şi numele
aplicaţiei. După crearea proiectului, se adaugă fişierele sursă în fereastra Project,
ce va apărea în partea de jos a ecranului. Adăugarea o puteţi face folosind tasta
Insert. De asemenea, dacă aţi inserat greşit o sursă (atenţie nu fişierele header se
includ), o puteţi şterge folosind tasta Delete ( după ce v-aţi poziţionat pe fişierul
în cauză). Puteţi insera surse scrise în limbaj de asamblare sau bilioteci obj sau lib.
while (k!=0);
}
long Suma(int X[],int N)
{ int i;
long S;
for(S=0,i=0;i<N;i++)
S+=X[i];
return S; }
int Minim(int X[],int N)
{ int i;
int Min;
Min=X[0];
for(i=0;i<N;i++)
if (Min>X[i])
Min=X[i];
return Min; }
int NumararePoz(int X[],int N)
{ int i;
245 Limbajul de programare C
int Nr;
Nr=0;
for(i=0;i<N;i++)
if (X[i]>0)
Nr++;
return Nr; }
int Cautare(int X[],int N,int Y) //intoarce pozitia sau -1 in ca ca nu gaseste
{ int i;
for(i=0;i<N;i++)
if (X[i]==Y)
return 1;
return -1; }
2. Fişierul cu extensia h, numit functii.h, va fi :
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
int Minim(int X[],int N);
int NumararePoz(int X[],int N);
int Cautare(int X[],int N,int Y);
În final, se va crea proiectul C folosind comanda Open din meniul Project şi se vor
include doar cele două fişiere sursă.
F1=fopen("Sali.txt", "w");
//Fisierul text ce va contine lista cu candidatii pe sali
//Ordonam alfabetic candidatii pentru a-i imparti pe sali,
//ordonand fizic fisierul
C = (Candidat *)malloc(sizeof(Candidat));
C1 = (Candidat *)malloc(sizeof(Candidat));
do
{ K=0;
fseek(F,0,0);
while (! feof(F))
{
fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fread(C1,sizeof(Candidat),1,F);
if (feof(F)) break;
Limbajul de programare C 250
if ((strcmp(C->Nume,C1->Nume)>0)||((strcmp(C->Nume,C1-
>Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(F,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,F);
fwrite(C,sizeof(Candidat),1,F);
K=1; }
fseek(F,-(long)sizeof(Candidat),1);
}
}
while (K);
I=1;
Nr=1;
fseek(F,0,0);
fprintf(F1,"\nSala numarul %d",Nr);
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fprintf(F1,"\n%3d%s %s%s",I,C->Nume,C->Init,C->Prenume);
I++;
if (I==21)
{ Nr++;
fprintf(F1,"\nSala numarul :%d",Nr);
I=1; }
}
fclose(F);
fclose(F1);
}
void ModificareDate(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
printf("\nDati numele candidatului pentru care se modifica datele:");
scanf("%s",Nm);
printf("\nDati prenumele candidatului pentru care se modifica datele:");
scanf("%s",Pn);
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
while (! feof(F))
251 Limbajul de programare C
{ fread(C,sizeof(Candidat),1,F);
if ((strcmp(Nm,C->Nume)==0) && (strcmp(C->Prenume,Pn)==0))
{ printf("\nObiect1 : ");scanf("%d",&C->Ob1);
printf("\nObiect2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);break; }
}
fclose(F);
}
void IntroducereNote(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
int Poz;
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
Poz=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F))
break;
printf("\nCandidatul %s %s %s",C->Nume,C->Init,C->Prenume);
printf("\nNota la obiectul 1 : ");scanf("%d",&C->Ob1);
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((C->MedieG < C1->MedieG)||((C->MedieG==C1->MedieG)&&((strcmp(C-Nume,
C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))))
{ fseek(G,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }
fseek(G,-(long)sizeof(Candidat),1);
}
253 Limbajul de programare C
}
while (K);
}
G1=fopen("Admisi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
admisi, fisier ce se poate tipari la imprimanta */
Nr=1;
printf("\nCandidati admisi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f",Nr,C->Nume,C->Init,C->Prenume,
C->Ob1,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf(""); //Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii admisi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("Nu avem candidati admisi");
}
void AfisareRespinsi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu respinsii
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii respinsi in fisierul respinsi.dat
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1<5) || (C->Ob2<5))
Limbajul de programare C 254
{ write(C,sizeof(Candidat),1,G);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati respinsi
{ //Ordonam alfabetic candidatii respinsi, ordonand fizic fisierul
G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat, nu mai trebuie ordonat
{
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((strcmp(C->Nume,C1->Nume)>0) || ((strcmp(C->Nume,C1-Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(G,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }
fseek(G,-(long)sizeof(Candidat),1);
}
}
while (K);
G1=fopen("Respinsi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
respinsi, fisier ce se poate tipari la imprimanta }*/
Nr=1;
printf("\nCandidati respinsi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f",Nr,C->Nume,C->Init,C->Prenume,
C->Ob1,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
255 Limbajul de programare C
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii respinsi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("\nNu avem candidati respinsi");
}
}
void main()
{ do
{ //clrscr();
printf("\nAlegeti optiunea:");
printf("\n1-Adaugare candidati");
printf("\n2-Afisare sali");
printf("\n3-Introducere note-calcul medie");
printf("\n4-Modificare note gresite-recalcularea mediei");
printf("\n5-Admisi");
printf("\n6-Respinsi");
printf("\n7-Iesire din program");
scanf("%d",&Optiune);
switch (Optiune)
{ case 1 : Adaugare();break;
case 2 : AfisareSali();break;
case 3 : IntroducereNote();break;
case 4 : ModificareDate();break;
case 5 : AfisareAdmisi();break;
case 6 : AfisareRespinsi();break; }
}
while (Optiune!=7);
}
Aplicaţia a fost realizată folosind un singur fişier sursă. Vom rescrie aplicaţia
folosind un proiect. Pentru aceasta vom împărţi sursa în două fişiere :
1. fişierul admitere.cpp, ce va conţine programul principal, prezentat în continuare:
#include <stdio.h>
Limbajul de programare C 256
#include <conio.h>
#include "functii.h"
void main()
{ int Optiune;
do
{ //clrscr();
printf("\nAlegeti optiunea:");
printf("\n1-Adaugare candidati");
printf("\n2-Afisare sali");
printf("\n3-Introducere note-calcul medie");
printf("\n4-Modificare note gresite-recalcularea mediei");
printf("\n5-Admisi");
printf("\n6-Respinsi");
printf("\n7-Iesire din program");
scanf("%d",&Optiune);
switch (Optiune)
{ case 1 : Adaugare();break;
case 2 : AfisareSali();break;
case 3 : IntroducereNote();break;
case 4 : ModificareDate();break;
case 5 : AfisareAdmisi();break;
case 6 : AfisareRespinsi();break; }
}
while (Optiune!=7);
}
void ModificareDate(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
printf("\nDati numele candidatului pentru care se modifica datele:");
scanf("%s",Nm);
printf("\nDati prenumele candidatului pentru care se modifica datele:");
scanf("%s",Pn);
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((strcmp(Nm,C->Nume)==0)&&(strcmp(C->Prenume,Pn)==0))
{ printf("\nObiect1 : ");scanf("%d",&C->Ob1);
259 Limbajul de programare C
printf("\nObiect2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);break;
}
}
fclose(F);
}
void IntroducereNote(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
int Poz;
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
Poz=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F))break;
printf("\nCandidatul %s %s %s",C->Nume,C->Init,C->Prenume);
printf("\nNota la obiectul 1 : ");scanf("%d",&C->Ob1);
printf("\nNota la obiectul 2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2.0;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);
Poz++;
fseek(F,(long)Poz*sizeof(Candidat),0);
}
fclose(F);
}
void AfisareAdmisi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu admisi
Limbajul de programare C 260
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii admisi in fisierul Admisi.dat
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1>=5) && (C->Ob2>=5))
{ fwrite(C,sizeof(Candidat),1,F);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati
admisi
{ G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat , nu mai trebuie ordonat
{
/*Ordonam descrescator candidatii admisi (adica au peste 5 la fiecare obiect
ordonand fizic fisierul */
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((C->MedieG < C1->MedieG)||((C->MedieG==C1->MedieG)&&((strcmp(C->
Nume ,C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&(strcmp(C->Prenume
,C1->Prenume)>0)))))
{ fseek(G,-
2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1;
}
261 Limbajul de programare C
fseek(G,-(long)sizeof(Candidat),1);
}
}
while (K);
}
G1=fopen("Admisi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
admisi, fisier ce se poate tipari la imprimanta */
Nr=1;
printf("\nCandidati admisi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f" ,Nr,C->Nume,C->Init,C->Prenume,C->Ob1
,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii admisi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("Nu avem candidati admisi");
}
void AfisareRespinsi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu respinsi
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii respinsi in fisierul respinsi.dat
Limbajul de programare C 262
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1<5) || (C->Ob2<5))
{ fwrite(C,sizeof(Candidat),1,G);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati
respinsi
{
//Ordonam alfabetic candidatii respinsi, ordonand fizic fisierul
G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat , nu mai trebuie ordonat
{
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ read(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((strcmp(C->Nume,C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(G,-
2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }
fseek(G,-(long)sizeof(Candidat),1);
}
}
while (K);
G1=fopen("Respinsi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
respinsi, fisier ce se poate tipari la imprimanta }*/
Nr=1;
263 Limbajul de programare C
printf("\nCandidati respinsi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f" ,Nr,C->Nume,C->Init,C->Prenume,C->Ob1,
C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii respinsi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("\nNu avem candidati respinsi");
}
}
3. fişierul cu prototipurile funcţiilor şi definiţia tipului de date candidat, fişier numit
funcţii.h:
typedef struct Cand { char Nume[20],Prenume[20];
char Init[3];
char Dn[11] ;
int Ob1,Ob2;
float MedieG; } Candidat;
void Adaugare(void);
void AfisareSali(void);
void ModificareDate(void);
void IntroducereNote(void);
void AfisareAdmisi(void);
void AfisareRespinsi(void);