Sei sulla pagina 1di 26

Informatică pentru clasa a X-a 1

Cap. 1 Tehnica programării modularizate

1. Noţiunea de subprogram

Prin subprogram se înţelege un ansamblu alcătuit din tipuri de date, variabile şi instrucţiuni
scrise în vederea unei anumite prelucrări (calcule, citiri, scrieri) şi care poate fi utilizat (rulat) doar dacă
este apelat de un program sau de un alt subprogram.
Subprogramul este cea mai mică unitate de program care poate fi compilată separat.
Exemplu: Se citeşte un vector cu n componente numere întregi. Se cere să se tipărească vectorul
sortat.
Această problemă poate fi rezolvată si clasic, utilizând funcţia main în care se vor realiza toate
prelucrările necesare pentru rezolvarea problemei:
- citeşte dimensiunea vectorului n
- citeşte elementele vectorului
- sortează vectorul utilizând unul din algoritmii cunoscuţi
- afişează vectorul sortat
De această dată vom încerca să rezolvăm problema utilizând tehnica programării modularizate,
adică ”spărgând” problema în subprobleme mai mici care vor fi rezolvate, fiecare, de câte un
subprogram. În acest caz, programul ar arăta astfel:
- apelează subprogramul care citeşte dimensiunea şi elementele vectorului
- apelează subprogramul care sortează vectorul
- apelează subprogramul care afişează vectorul
Utilizând acestă metodă, vom obţine următoarea structură de program:

funcţia main()

funcţia citire(v,n) funcţia sortare(v,n) funcţia scriere(v,n)

În structura modulară de mai sus am notat cu v vectorul supus prelucrării, iar cu n dimensiunea
lui, adică numărul de elemente.
În general, o problemă complexă se rezolvă mai uşor dacă o descompunem în altele mai
simple; şansele de a greşi la scrierea unui subprogram sunt mai mici decât acelea de a greşi la scrierea
unui program mare. Acesta din urmă rezultă din asamblarea subprogramelor la care se adaugă,
eventual, câteva linii scrise în programul principal. Avantajele utilizării subprpogramelor sunt:
- reutilizarea codului : o dată scris, un subprogram poate fi utilizat de mai multe programe
- elaborarea algoritmilor prin descompunerea problemei în altele mai simple; în acest fel putem
rezolva mai uşor problema, eventual prin colaborarea unei echipe de programatori
- reducerea numărului de erori care pot apărea la scrierea programelor
- depistarea cu uşurinţă a erorilor : verificăm subprogramele, apoi modul în care le-am asamblat
În limbajul C/C++ subprogramele sunt de tip funcţie, inclusiv funcţia main este un subprogram,
primul care se execută la rularea unui program.
2 Informatică pentru clasa a X-a

2. Structura funcţiilor şi apelul lor

2.1 Generalităţi
În esenţă, o funcţie are structura:
antet
instrucţiune compusă
a) Anteul conţine mai multe informaţii importante necesare compilatorului şi anume: numele
funcţiei, lista parametrilor formali, tipul rezultatului.
Structura antetului este:
tip nume(lista parametrilor formali)
Lista parametrilor formali este de forma:
parametru1,parametru2,………..,parametrun
Fiecare parametru are forma:
tip nume
Observaţie: Există şi posibilitatea ca lista parametrilor formali să fie vidă.

b) Instrucţiunea compusă cuprinde declaraţiile variabilelor locale şi instrucţiunile propriu-zise


care descriu operaţiile realizate de către funcţie.

Observaţii:
- poate fi tip al unei funcţii orice tip de dată cu excepţia tablourilor
- dacă ţinem neapărat, există posibilitatea ca funcţia să întoarcă tablouri, dacă acestea sunt
înglobate în tipuri declarate cu struct, aşa cum se va vedea într-un paragraf ulterior.
Exemple de antete de funcţii:
- int suma(int a,int b) – funcţia se numeşte suma, returnează un rezultat de tip int şi are doi
parametri formali de tip int, numiţi a şi b
- void t(int n,float v[20]) – funcţia se numeşte t, este de tip void (nu returnează rezultat
prin nume), are doi parametri formali, primul numit n, de tip int, al doilea numit v, de tip float*
(pointer la float)
- char* sir(int n,char a[200]) – funcţia se numeşte sir, întoarce un pointer către un şir de
caractere şi are doi parametri formali, unul de tip int, numit n şi altul de tip char* (pointer la şir de
caractere) numit a
- elev mana(int n,char n[200]) – funcţie de tip elev, unde elev este de tipul structură
următor:
struct elev{
char nume[20];
int varsta;
};

Observaţii:
- O funcţie returnează rezultatul la întâlnirea instrucţiunii return, care are forma return
expresie; Trebuie ca tipul expresiei să coincidă cu tipul funcţiei sau să poată fi convertit implicit
către acesta. La întâlnirea instrucţiunii return, după atribuirea valorii, execuţia funcţiei se încheie şi
se revine la funcţia care a apelat-o. În absenţa instrucţiunii return, execuţia funcţiei se încheie după
execuţia ultimei instrucţiuni. În acest caz nu se întoarce nici-o valoare (cazul funcţiilor de tip void).
- O funcţie poate fi apelată de sine stătător (prin nume şi lista parametrilor efectivi), dar poate fi
inclusă şi în cadrul expresiilor, caz în care, la evaluarea expresiei este apelată. Acestă ultimă formă
de apel nu este valabilă în cazul funcţiilor de tip void.
Exemplu:
#include<stdio.h>
int prod(int x,int y)
{
return x*y;
}
void main()
{
int x=4,y=9;
printf(“%d”,7+prod(x,y));
}
Informatică pentru clasa a X-a 3

Apelul funcţiei s-a realizat din interiorul expresiei 7+prod(x,y)


Observaţii:
- în cadrul expresiei, apelul este un operand; el intră în calcul cu valoarea returnată de funcţie
- după apelul funcţiei se continuă evaluarea expresiei
- la apel, ordinea de evaluare a parametrilor nu este definită; de exemplu, la apelul test(2-3,2+3) nu
ştim dacă înainte se efectuează scăderea sau adunarea

2.2 Declararea variabilelor


2.2.1 Noţiuni generale

Până în prezent am declarat variabile doar în corpul funcţiilor, inclusiv în cel al funcţiei main().
Variabilele astfel declarate se numesc locale. Sistemul de operare alocă fiecărui program trei zone
distincte în memoria internă a calculatorului, în care se găsesc memorate variabilele programului. Harta
simplificată a memoriei interne este prezentată în continuare:

segment de date
segment de stivă
heap

Segmentul de date este folosit pentru stocarea codului programului aflat în execuţie şi pentru
variabilele care sunt folosite de către acesta pe toată durata execuţiei. Segmentul de stivă este utilizat
pentru a realiza transferul parametrilor la apelul subprogramelor şi pentru a controla execuţia
programului în cazul lucrului cu subprograme. Zona heap se numeşte şi zonă de accumulare a
variabilelor dinamice şi se foloseşte pentru alocarea variabilelor dinamice, create la cererea expresă a
programatorului, în timpul execuţiei unui program.
De asemenea, în limbajul C, există posibilitatea ca variabilele să fie memorate într-un anumit
registru al microprocesorului. În acest caz, timpul de acces la astfel de variabile este foarte mic, deci se
pot obţine programe optimizate. Numărul variabilelor care pot fi memorate în regiştrii interni este însă
redus.
În general o variabilă se caracterizează prin patru atribute şi anume:
- clasa de memorare
- vizibilitatea
- durata de viaţă
- tipul variabilei, singurul studiat până în acest moment
Clasa de memorare precizează locul unde este memorată variabila respectivă. O variabilă
poate fi memorată în segmentul de date, în cel de stivă, în heap sau într-un registru al
microprocesorului.
Vizibilitatea precizează liniile textului sursă din care variabila respectivă poate fi accesată.
Astfel avem:
- vizibilitate la nivel de bloc (instrucţiune compusă)
- vizibilitate la nivel de fişier, în cazul în care programul ocupă doar un singur fişier sursă (singurul
caz tratat momentan)
- vizibilitate la nivel de clasă, legată de programarea orientată pe obiecte, facilitate specifică în C++
Durata de viaţă reprezintă timpul în care variabila respectivă are alocat spaţiu în memoria
internă (deci există efectiv). Astfel avem:
- durată statică, conform căreia variabila are alocat spaţiu în tot timpul execuţiei programului
- durată locală, conform căreia variabila are alocat spaţiu în timpul în care se execută înstrucţiunile
blocului respectiv (blocul care conţine declaraţia variabilei)
- durată dinamică, caz în care alocarea şi dealocarea spaţiului necesar variabilei respective se face
explicit de către programator, în timpul execuţiei programului, prin folosirea unor operatori şi funcţii
speciale (de exemplu, new, malloc() etc.)
În limbajul C/C++ variabilele pot fi împărţite în trei mari categorii: locale, globale şi dinamice.

2.2.2 Variabile globale

Aceste variabile se declară în afara corpului oricărei funcţii, ca în exemplul următor:


#include<stdio.h>
int a;
void t()
4 Informatică pentru clasa a X-a

{
a=3;
printf(“%d”,a);
}
int b;
void main()
{
b=4;
printf(“%d”,b);
t();
}
Variabilele a şi b sunt globale. Ele pot fi utilizate de toate funcţiile care urmează în textul sursă
declaraţiei variabilei respective. Deoarece pot fi utilizate, deci implicit şi modificate, de toate funcţiile
care succed momentul declaraţiei, aceste variabile se numesc globale.

Observaţie: La declarare, variabilele globale sunt iniţializate de către compilator cu 0.

Atributele variabilelor globale sunt:


- clasa de memorare este segmentul de date
- vizibilitatea: sunt vizibile în toate funcţiile care le succed; în exemplul anterior, a poate fi acccesată
din corpul oricărei funcţii, în timp ce variabila b poate fi accesată numai din funcţia main
- durata de viaţă este statică, adică au spaţiu de memorie rezervat în tot timpul execuţiei programului

2.2.3 Variabile locale

Aceste variabile sunt declarate în corpul funcţiilor, mai precis, pot fi declarate în orice bloc
(instrucţiune compusă) al acestora.

Observaţie: Variabilele declarate în funcţia main() sunt tot variabile locale, deci sunt vizibile numai în
această funcţie.

În exemplul următor, variabila a este declarată în corpul funcţiei t(), iar variabila b este
declarată în corpul funcţiei main():
void t()
{
int a=3;
printf(“%d”,a+2);
}
void main()
{
int b=4;
printf(“%d”,b--);
}

Atributele variabilelor locale sunt:


- clasa de memorare este, implicit, segmentul de stivă. Există posibilitatea ca acestea să fie alocate
şi în registrele microprocesorului, caz în care declaraţia lor trebuie precedată de cuvântul cheie
register, ca în exemplul următor: register int b=4;
- vizibilitatea variabilelor locale este la nivelul blocului în care au fost declarate.
- durata de viaţă a variabilelor locale este atât timp cât durează execuţia blocului respectiv, adică
este locală

Observaţie: Variabilele locale nu sunt iniţializate implicit de către compilator cu 0, această sarcină
revenind programatorului. În cazul în care iniţializarea este omisă, ele conţin o valoare oarecare numită
valoare reziduală care poate duce la rezultate eronate.

Exemplul 1: În funcţia următoare am declarat două variabile de tip int, numite b şi c. Variabila b este
vizibilă la nivelul funcţiei, dar variabila c este vizibilă doar la nivelul blocului în care a fost declarată.
Ambele sunt variabile locale alocate pe segmentul de stivă.
void func()
{
int b=7; // vizibila in toata functia
{
Informatică pentru clasa a X-a 5

int c=9; // vizibila numai in acest bloc (instr.compusa)


printf(“%d %d”,b,c);
}
}
Exemplul 2: În programul următor am declarat trei variabile, toate numite a. Una este globală, iar
două sunt locale, dar declarate în blocuri diferite.
#include<stdio.h>
int a; // variabila globala,vizibila in orice punct din program
void func()
{
int a=4; // variabila locala,vizibila in toata functia
{
int a=3; // variabila locala,vizibila numai in acest bloc
printf(“%d”,a); // tipareste a=3
a+=2;
}
printf(“%d”,a); // tipareste a=4
a++;
}
void main()
{
a=5; // refera variabila globala a
func();
printf(“%d”,a); // tipareste a=5
}

Observaţie: În cazul în care, într-un anumit bloc sunt vizibile (se pot accesa) mai multe variabile cu
acelaşi nume, dar cu domenii de vizibilitate diferite, se accesează variabila cu vizibilitatea cea mai
mică. Este cazul variabilei a din blocul interior funcţiei func() pentru care se consideră cea mai mică
vizibilitate (cea de bloc) şi se tipăreşte valoarea 3.

Observaţie: Durata de viaţă a unei variabile locale poate fi modificată folosind atributul static, astfel
încât, deşi respectiva variabilă nu este recunoscută în afara funcţiei, ea nu moare adată cu revenire
dintr-un apel. Cu alte cuvinte, apeluri succesive vor regăsi, fiecare, valoarea lăsată de apelul anterior.
Exemplu: În urma execuţiei programului următor, variabila m va avea valoarea 3, deoarece cele două
apeluri succesive ale funcţiei f() vor returna valorile 1, respectiv 2, variabila locală a având atributul
static.
#include<stdio.h>
int f()
{
static int a=1;
return a++;
}
void main()
{
int m;
m=f()+f();
printf(“%d”,m);
}

2.3 Transmiterea parametrilor


Parametrii care se găsesc în antetul funcţiei se numesc parametri formali, iar cei care se
găsesc în instrucţiunea de apel se numesc parametri efectivi.
Exemplu: Funcţia următoare calculează suma a două numere naturale.
#include<stdio.h>
int suma(int a,int b)
{
return a+b;
}
void main()
6 Informatică pentru clasa a X-a

{
int c=4,d=3;
printf(“%d\n”,suma(2,3));
printf(“%d\n”,suma(2+7,3-1*2);
printf(“%d\n”,suma(c,d);
printf(“%d\n”,suma(1.9,3.3);
}
Parametrii formali ai funcţiei sunt a şi b. Funcţia este apelată de mai multe ori. Parametrii
efectivi sunt pe rând:
- 2, 3
- 2+7, 3-1*2
- c, d
- 1.9, 3.3
După fiecare apel al funcţiei se tipăreşte suma obţinută. Între parametrii formali şi cei efectivi
trebuie să existe o anumită concordanţă care este descrisă prin regulile următoare:
- Numărul parametrilor formali trebuie să coincidă cu numărul parametrilor efectivi. La această regulă
există şi excepţii. În exemplul dat numărul parametrilor formali este 2, iar cel al parametrilor efectivi
tot 2.
- Tipul parametrilor efectivi trebuie să coincidă cu tipul parametrilor formali sau tipul parametrilor
efectivi să poată fi convertit implicit către tipul parametrilor formali. La apelul suma(2,3); parametrii
efectivi sunt constante de tip întreg şi coincid cu tipul parametrilor formali. La apelul suma(2+7,3-
1*2); parametrii efectivi sunt expresii de tip întreg. În acest caz, înainte de apel se evaluează
expresiile respective. La apelul suma(c,d); parametrii formali sunt variabile de tip întreg date prin
valorile lor. La apelul suma(1.9,3.3); parametrii efectivi sunt constante de tip real care se
convertesc la tipul int prin trunchiere şi suma calculată este 1+3=4.

Observaţie: Nu este obligatoriu ca numele parametrilor formali să coincidă cu numele parametrilor


efectivi şi, în multe situaţii nici nu este recomandabil, pentru a evita confuziile.

În momentul lansării în execuţie a unei funcţii (la apel), parametrii efectivi se transmit funcţiei
apelate după următoarele reguli:
- Pentru memorarea parametrilor efectivi subprogramele folosesc segmentul de stivă, întocmai ca
pentru variabilele locale.
- Memorarea parametrilor efectivi transmişi se face în ordinea în care aceştia figurează în antet, de
la stânga spre dreapta.
- În cadrul subprogramului, parametrii transmişi în momentul apelului şi memoraţi pe stivă sunt
variabile. Numele lor este cel din lista parametrilor formali. Aceste variabile se comportă ca nişte
variabile locale subprogramului, deci nu sunt vizibile în afara funcţiei şi există cât timp funcţia este
în execuţie.
- La revenirea în blocul apelant, conţinutul variabilelor memorate pe stivă se pierde, durata de viaţă a
variabilelor locale fiind locală.
Există două mecanisme de transmitere a parametrilor, transmiterea prin valoare şi transmiterea prin
referinţă.

2.3.1 Transmiterea prin valoare

Se utilizează atunci când suntem interesaţi ca subprogramul să lucreze cu acea valoare, dar să
nu poată modifica parametrul efectiv corespunzător din blocul apelator.
Se pot transmite prin valoare:
1. Valorile reţinute de variabile. În acest caz parametrii efectivi trebuie să fie numele
variabilelor. Exemplu:
#include<stdio.h>
void test(int n)
{
n++;
printf(“n=%d\n”,n); // tipareste n=8
}
void main()
{
int n=7;
test(n);
printf(“n=%d\n”,n); // tipareste n=7
}
Informatică pentru clasa a X-a 7

Parametrul n este transmis prin valoare. În funcţia main() acest parametru este iniţializat cu
valoarea 7. Când apelăm funcţia test(), se rezervă spaţiu pe stivă, spaţiu care are numele
parametrului formal (în acest caz, tot n) şi care este iniţializat cu valoarea memorată de
variabila n a programului principal. Altfel spus, pe stivă se copie valoarea parametrului efectiv
de apel. În funcţie, variabila n (care este locală acestei funcţii) este incrementată şi devine 8,
valoare care va fi tipărită. La ieşirea din funcţie, variabila n din stivă se pierde, adică nu mai are
spaţiu alocat, prin urmare valoarea 8 este pierdută. În main() se tipăreşte valoarea variabilei n
(locală acesteia) care are valoarea 7.
Pentru exemplul anterior, conţinutul stivei, în momentul apelului şi după execuţia
funcţiei test(), este următorul:

n=7, parametru valoare


de apel, local funcţiei
test
n=7 , var. locală funcţiei adresa(n) n=7 , var. locală funcţiei
main() main()
adresa de revenire din adresa de revenire din
funcţia test funcţia test

în momentul apelului după execuţia funcţiei test

Se observă că, în momentul apelului funcţiei test(), pe stivă sunt alocate două variabile cu acelaşi
nume n. Prima variabilă este variabila locală funcţiei main() care se salvează pe stivă în momentul
apelului pentru a putea reface contextul funcţiei main() după încheierea apelului. A doua variabilă
este parametrul formal tip valoare n, vizibil numai în funcţia test() şi iniţializat în momentul apelului
cu valoarea 7. Indiferent ce valori primeşte acest n în corpul funcţiei test(), după încheierea
execuţiei acestei funcţii, spaţiul său este dealocat din stivă, adică variabila respectivă este distrusă.
Din acest motiv, după execuţia funcţiei test(), conţinutul stivei este cel din dreapta. Se reface
contextul din care s-a lansat apelul funcţiei test(), adică se recuperează din stivă valoarea variabilei
locale n=7 şi adresa de revenire, adică adresa instrucţiunii printf.

2. Expresii. În acest caz, parametrii efectivi sunt expresii, care pot conţine şi funcţii şi care mai
întâi se evaluează. Exemplu:
#include<stdio.h>
#include<math.h>
void test(int n)
{
printf(“n=%d\n”,n);
}
void main()
{
test(5); // se va tipari 5
test(7+(int)sqrt(45)); // se va tipari 13
}
În funcţie se crează o variabilă numită n, reţinută pe stivă, care la primul apel va primi valoarea
5 şi la al doilea apel valoarea 13. La ieşirea din funcţie conţinutul acestei variabile se pierde.

Transmiterea parametrilor prin valoare se utilizează când nu dorim ca subprogramul apelat să


poată modifica parametrii efectivi de apel. Acesta este modul implicit de transmitere a parametrilor în
limbajul C. Dacă nu ar exista decât transmiterea prin valoare, ar fi imposibil să modificăm valoarea
anumitor valori care sunt declarate în blocul apelator. Acest lucru este totuşi posibil dacă lucrăm cu
variabile de tip pointer, care, aşa cum ştim deja, conţin adresele de memorie ale altor variabile din
program. Având acces la locaţia de memorie a unei variabile (prin adresa ei), îi putem desigur modifica
conţinutul. Funcţia următoare realizează interschimbarea valorilor a două variabile din programul
principal. Pentru a putea face acest lucru, funcţia primeşte adresele de memorie ale celor două
variabile, adică doi pointeri.
#include<stdio.h>
void schimba(int *x,int *y)
{
int aux;
8 Informatică pentru clasa a X-a

aux=*x;
*x=*y;
*y=aux;
}
void main()
{
int a=7, b=12;
schimba(&a,&b);
printf(“a=%d\t b=%d”,a,b); // tipareste a=12, b=7
}
Se observă că parametrii formali sunt doi pointeri la întreg care în momentul apelului primesc ca valori
adresa variabilei a, respectiv b (obţinute prin aplicarea operatorului &). În limbajul C acesta este
singurul mod prin care se pot transmite parametrii prin adresă, deci modifica de către un subprogram
apelat.
Pentru exemplul anterior, evoluţia stivei este:

y=&b, parametru formal de tip


pointer, local funcţiei schimba() b=7, variabilă locală funcţiei
x=&a, parametru formal de tip main()
pointer, local funcţiei schimba()
b=12, variabilă locală funcţiei a=12, variabilă locală funcţiei
adresa(a)
main() main()
a=7, variabilă locală funcţiei adresa(b) adresa de revenire din funcţia
main() schimba()
adresa de revenire din funcţia
schimba()

în momentul apelului funcţiei schimba() după execuţia funcţiei schimba()

În cazul transmiterii prin adresă, pe stiva procesorului se depun, pe lângă adresa de revenire şi
valorile variabilelor locale funcţiei main(), necesare pentru a reface contextul din care s-a făcut apelul,
şi adresele de memorie ale parametrilor efectivi corespunzători. Deşi pointerii x şi y sunt locali funcţiei
schimbă() şi sunt dealocaţi de pe stivă în momentul în care execuţia acestei funcţii se încheie, orice
modificare a locaţiilor de memorie adresate de aceşti pointeri se păstrează şi după încheierea apelului.
Din acest motiv, după execuţia funcţiei schimbă(), conţinutul stivei este cel din dreapta. Valorile
variabilelor a şi b, locale funcţiei main(), au fost modificate în timpul execuţiei funcţiei schimba() care a
avut acces la adresele lor de memorie. La revenirea din funcţie, se recuperează din stivă noile valori
ale variabilelor a şi b, adresa instrucţiunii printf şi se continuă execuţia funcţiei main().

Observaţie: Există o excepţie de această regulă, şi anume, când parametrul este un tablou. În C,
numele unui tablou este un pointer către componentele tabloului. Când transmitem prin valoare un
tablou, ceea ce primeşte subprogramul este un pointer către elementele acestuia, adică adresa de
început a tabloului în memorie. Având acces la această adresă, subprogramul poate modifica valorile
reţinute în tablou. Practic, în cazul tablourilor, transmiterea prin valoare devine transmitere prin adresă.

2.3.2 Transmiterea tablourilor către funcţii

Fie următorul fragment de program:


void main()
{
int a[10];
......
func(a); // apelul unei functii cu parametrul a
.....
}
Dacă o funcţie primeşte un tablou unidimensional, atunci parametrul formal al acesteia poate fi declarat
în trei moduri: ca pointer, ca tablou dimensionat sau ca tabloul nedimensionat. De exemplu, putem
declara funcţia func() în următoarele moduri:
void func(int *x) // parametru formal de tip pointer
{
Informatică pentru clasa a X-a 9

………
}
sau ca:
void func(int x[10]) // tablou dimensionat
{
…..
}
sau ca:
void func(int x[]) // tablou nedimensionat
{
……
}
Toate cele trei metode de declarare determină rezultate asemănătoare, deoarece indică compilatorului
că urmează să fie primit un pointer la întreg. Dimensiunea tabloului nu contează din punctul de vedere
al funcţiei, deoarece C nu verifică depăşirea limitelor unui tablou. Din punct de vedere funcţional,
declaraţia:
void func(int x[32])
{
......
}
este de ademenea funcţională, deoarece compilatorul C generează un cod care cere funcţiei func() să
primească un pointer la întreg, fără a crea efectiv un tablou cu 32 de elemente.
Metoda de transmitere este aceeaşi şi când un tablou bidimensional este folosit ca argument al
unei funcţii. În acest caz se transmite efectiv numai pointerul către primul element. Cu toate acestea,
parametrul care primeşte un tablou bidimensional trebuie să definească măcar dimensiunea din
dreapta a tabloului. Aceasta deoarece compilatorul C trebuie să cunoască lungimea fiecărui rând
pentru a aplica corect indicii fiecărui termen al matricii. De exemplu, o funcţie care primeşte o matrice
de 10*10 întregi va fi declarată astfel:
void func(int x[][10])
{
....
}
Se poate preciza şi dimensiunea din stânga, dar nu este absolut necesar.

2.3.3 Transmiterea structurilor către funcţii

Când membrul unei structuri este transmis unei funcţii, ceea ce se transmite funcţiei este de
fapt valoarea acelui membru. Ca atare, se transmite doar o variabilă. Fie structura următoare:
struct info
{
char x;
int y;
float z;
char s[10];
}set;
Iată exemple de transmitere ale fiecărui membru către o funcţie:
func(set.x); // transmite valoarea caracter a lui x
func(set.y); // transmite val. intreaga a lui y
func(set.z); // transmite val. virgula mobila a lui z
func(set.s); // transmite adresa sirului s
func(set.s[3]); // transmite val. tip caracter a lui s[3]
Dacă dorim să transmitem adresa unui anumit membru al unei structuri, inserăm operatorul & înaintea
numelui structurii.
func(&set.x); // transmite adresa caracterului x
func(&set.y); // transmite adresa intreagului y
func(&set.z); // transmite adresa variabilei float z
func(set.s); // transmite adresa sirului s
func(&set.s[3]); // transmite adresa caracterului s[3]
Când o structură este folosită ca argument al unei funcţii, se va efectua transmiterea întregii structuri,
folosind metoda standard a apelului prin valoarea. Aceasta înseamnă că structura nu va putea fi
modificată. Tipul structurii trebuie să coincidă cu tipul parametrului formal care primeşte structura. Din
10 Informatică pentru clasa a X-a

acest motiv, declaraţia tipului structurii trebuie să aibă caracter global, pentru a putea fi folosită de către
toate funcţiile din program, ca în exemplul următor:
#include<stdio.h>
struct data{
int a,b;
char ch;
}; // declaratie globala de tip
void func(data x) // foloseste tipul global
{
printf(“%d”,x.a);
}
void main()
{
data arg; // foloseste tipul global
arg.a=157;
func(arg);
}
Deoarece limbajul C permite declararea pointerilor către structuri, putem modifica un argument de tip
structură folosind mecanismul transmiterii prin adresă, cu ajutorul pointerilor ca în exemplul următor:
#include<stdio.h>
struct ora{
int ore;
int minute;
int secunde;
}; // declaratie globala
void func(ora *p) // pointer la structura globala
{
…………….
}
void main()
{
ora arg;
…....
func(&arg); // transmite adresa structurii
}

Observaţie: Poate fi tip al unei funcţii orice tip de dată cu excepţia tablourilor. Dacă ţinem neapărat,
există posibilitatea ca funcţia să întoarcă tablouri, dacă acestea sunt înglobate în tipuri declarate cu
struct, aşa cum se arată în exemplul următor:
#include<stdio.h>
struct mat{
float matrice[6][8];
};
mat citire(int m,int n)
{
mat a;
int i,j;
for(i=0;i<m;i++)
for(j=0;j<n;j++) scanf(“%f”,&a.matrice[i][j]);
return a;
}
void main()
{
int i,j;
mat mtr=citire(3,2);
for(i=0;i<3;i++)
{
for(j=0;j<2;j++) printf(“%6.2f”,mtr.matrice[i][j]);
printf(“\n”);
}
}
Informatică pentru clasa a X-a 11

2.3.4 Transmiterea prin referinţă

În limbajul C++ există un tip special de date care permite ca o variabilă să fie identificată prin
mai multe nume, tip numit referinţă. Aparent, programul lucrează cu mai multe variabile, dar de fapt toţi
identificatorii respectivi referă aceeaşi locaţie de memorie. Declararea unei variabile de tip referinţă se
face cu sintaxa:
tip& nume variabilă=valoare de iniţializare;
În programul următor, variabila x este o referinţă pentru variabila y. Ele au aceeaşi valoare şi aceeaşi
adresă de memorie, dar poartă nume diferite.
#include<stdio.h>
void main()
{
int x=7;int& y=x; // sau int x=7, &y=a;
printf(“x=%d si are adresa %p\n”,x,&x);
printf(“y=%d si are adresa %p\n”,y,&y);
}
În cazul subprogramelor, parametrii sunt transmişi prin referinţă atunci când ne interesează ca
la revenirea din subprogram variabila transmisă să reţină valoarea stabilită în timpul execuţiei
subprogramului.
În cazul transmiterii prin referinţă parametrii formali trebuie să fie referinţe la variabile. În acest
caz subprogramul reţine pe stivă adresa variabilei transmisă prin referinţă şi având acces la adresa de
memorie a parametrului efectiv corespunzător, îl poate modifica.
Exemplu: Programul următor interschimbă valorile a două variabile transmise prin referinţă.
#include<stdio.h>
void schimba(int &a, int &b)
{
int aux=a; a=b; b=aux;
}
void main()
{
int x=7, y=5;
schimba(x,y);
printf(x=%d\ty=%d\n”,x,y);
}

Concluzii:
a) În momentul în care într-un subprogram este întâlnită instrucţiunea de apel a unui alt subprogram,
controlul execuţiei este cedat subprogramului apelat şi pe stivă se depun, în ordine:
- adresa de revenire din subprogramul apelat
- valorile variabilelor locale subprogramului apelator
- valorile parametrilor transmişi prin valoare
- adresele parametrilor transmişi prin referinţă
b) Revenirea din subprogramul apelat se face fie la întâlnirea instrucţiunii return (cazul funcţiilor care
returnează un rezultat de un anumit tip), fie la întâlnirea acoladei care încheie instrucţiunea compusă a
subprogramului apelat (cazul funcţiilor de tip void)
c) La încheierea execuţiei subprogramului apelat se recuperează din stivă, în ordine, valorile
variabilelor locale subprogramului apelator şi adresa de revenire, adică se reface contextul din care s-a
făcut apelul.

2.4 Definirea şi declararea unui subprogram

Deşi aparent asemănătoare, cele două noţiuni diferă. A defini un subprogram, înseamnă a-l
scrie efectiv (antet şi instrucţiune compusă). Este foarte important locul în care se defineşte un
subprogram. A declara un subprogram, înseamnă a-l anunţa. Un subprogram nedeclarat nu poate fi
folosit. Definiţia unui subprogram ţine loc şi de declaraţie.
Exemplu: Programul următor conţine două funcţii f1 şi f2. Definiţiile ambelor funcţii se găsesc înaintea
funcţiei main() şi din acest motiv ele pot fi apelate din main(). Definiţia funcţiei f1 este înaintea definiţiei
12 Informatică pentru clasa a X-a

lui f2, deci funcţia f1 poate fi apelată din f2, în schimb, din f1 nu poate fi apelată f2 pentru că declaraţia
acesteia îi succede lui f1.
#include<stdio.h>
void f1()
{
printf(“functia f1\n”);
}
void f2()
{
f1();
printf(“functia f2\n”);
}
void main()
{
f1();
f2();
}
Dacă dorim ca f1 să poată apela funcţia f2 folosim prototipul funcţiei care este antetul acesteia urmat
de punct şi virgulă. Prototipul are rolul de a declara o funcţie şi nu conţine definiţia acesteia.
Exemplu:
#include<stdio.h>
void f1(); // prototip f1
void s2(); // prototip f2
void main()
{
f1();
}
void f1() // definitia lui f1
{
f2();
printf(“functia f1\n”);
}
void f2() // definitia lui f2
{
f1();
printf(“functia f2\n”);
}

Observaţie: În prototipul unei funcţii se poate omite numele parametrilor formali, precizându-se numai
tipul acestora. Spre exemplu, este corect un prototip de forma float func(int, int&); care declară o
funcţie cu rezultat de tip float si doi parametri, unul valoare de tip int şi unul referinţă la int.
În C++ este o practică uzuală să se scrie mai întâi prototipurile tuturor funcţiilor utilizate în
program, fără funcţia main(), iar după funcţia main() să fie definite aceste funcţii. În acest fel, orice
funcţie, mai puţin main(), poate fi apelată din oricare alta.

2.5 Funcţii cu număr variabil de argumente

O facilitate importantă a limbajului C este posibilitatea utilizării funcţiilor cu un număr variabil de


argumente. Este obligatoriu ca cel puţin primul parametru să apară în lista parametrilor formali. Pentru
parametrii variabili antetul va conţine “…”. Parametrii efectivi sunt depuşi, unul după altul, în ordinea
declarării în antet, în stivă. Dacă la adresa primului parametru adunăm 1, obţinem adresa următorului
parametru şi aşa mai departe. De regulă, parametrii ficşi conţin informaţii despre cei variabili (numărul
lor, în unele cazuri tipul lor).
În exemplul următor, funcţia f are un număr neprecizat de parametri, aspect semnalat prin
prezenţa celor trei puncte după parametrul fix n în antetul funcţiei. Accesarea parametrilor actuali în
momentul apelului se bazează pe faptul că parametrii se memorează la adrese consecutive în stivă.
Astfel, se pleacă de la adresa ultimului parametru fix, în acest exemplu &n. Valoarea &n+1 reprezintă
adresa următorului parametru, adică a primului parametru variabil, &n+2 este adresa celui de-al doilea
parametru variabil etc. În felul acesta, valorile care vor apărea la apel după parametrii ficşi vor putea fi
Informatică pentru clasa a X-a 13

accesaţi printr-un calcul obişnuit de adrese şi funcţia va returna maximul dintre elementele vectorului x,
adică 23.
#include<stdio.h>
int f(int n,…)
{
int *x=&n+1, m=x[0], j;
for(j=1;j<n;j++)
x[j]>m? m=x[j]: m=m;
return m;
}
void main()
{
printf(“%d”,f(4,12,11,23,2);
}

Un alt aspect important referitor la funcţiile limbajului C este posibilitatea de a iniţializa


parametrii formali ai unei funcţii direct în antetul acesteia. În acestă situaţii, la apelul funcţiei pot să
lipsească unul sau mai mulţi parametri actuali, chiar toţi parametrii. În cazul în care la apel lipseşte un
parametru actual, compilatorul va folosi în funcţie valoarea cu care a fost iniţializat la declarare
parametrul formal aferent.
Exemplu: Programul următor este corect şi va afişa valorile m=6, n=15, p=34 şi q=52.
#include<stdio.h>
int f(int a=1,int b=2,int c=3)
{
return a+b+c;
}
void main()
{
int m,n,p,q;
m=f();
n=f(10);
p=f(12,19);
q=f(11,23,18);
printf(“%5d%5d%5d%5d”,m,n,p,q);
}

2.6 Folosirea funcţiilor ca parametri de apel

Limbajul C permite transmiterea funcţiilor ca parametri de apel pentru alte funcţii. Numele
funcţiei este dat ca parametru prin intermediul unui pointer către funcţie. Considerăm programul
următor:
#include<stdio.h>
#include<math.h>
float ma(int x,int y)
{
return (x+y)/2.;
}
float mg(int x,int y)
{
return sqrt(x*y);
}
void calcul(int x,int y,float (*f)(int,int))
{
printf(“%.1f”,f(x,y));
}
void main()
{
int a,b;
scanf(“%d %d”,&a,&b);
if(a>=b) calcul(a,b,ma);
14 Informatică pentru clasa a X-a

else if(a>0 && b>0)


calcul(a,b,mg);
}

În acest exemplu, funcţia calcul are un antet mai deosebit. Primii doi parametri sunt doi întregi x
şi y. Al treilea parametru, float (*f)(int,int) , reprezintă un pointer către o funcţie f. Această funcţie f are
la rândul ei doi parametri de tipul int. Nu este necesar să se precizeze numele acestor parametri, ci
doar tipul lor. Instrucţiunea printf(“%.1f”,f(x,y)); din corpul funcţiei f afişează valoarea întoarsă de
funcţie. În funcţia main() se fac apelurile: calcul(a,b,ma);, respectiv calcul(a,b,mg);. La apel, al treilea
parametru din antet (pointerul la funcţie), este înlocuit cu numele unei funcţii, ma, respectiv, mg. Aceste
funcţii au fost scrise anterior. Spre exemplu, dacă se citesc valorile a=7 şi b=6, identificatorul f este
înlocuit cu ma, se calculează media aritmetică a parametrilor x şi y şi se tipăreşte valoarea 6.5. Dacă
se citeşte a=2 şi b=8, identificatorul f se înlocuieşte cu mg, se calculează media geometrică a
parametrilor x şi y şi se tipăreşte valoarea 4.

3. Exemple de aplicaţii care folosesc subprograme


Exemplul 1: Se citesc două numere întregi m şi n. Să se tipărescă numerele prime aflate în intervalul
[m,n].
Pentru a verifica primalitatea unui număr din intervalul dat vom folosi o funcţie care returnează
1 dacă numărul respectiv este prim, şi 0 în caz contrar.
#include<stdio.h>
#include<math.h>
int prim(int x)
{
int i;
for(i=2;i<=sqrt(x);i++)
if(!(x%i)) return 0;
return 1;
}
void main()
{
int m,n,i;
printf("m,n="); scanf("%d %d",&m,&n);
printf(" numerele prime din intervalul [%d,%d] sunt:\n",m,n);
for(i=m;i<=n;i++)
if(prim(i)) printf("%6d",i);
printf("\n");
}

Exemplul 2: Se citeşte un vector cu n componente numere întregi. Se cere să se tipărescă cmmdc al


elementelor vectorului.
Pentru citirea vectorului vom scrie o funcţie care returnează (prin referinţă) numărul n al
elementelor şi elementele propriu-zise ale vectorului, citite de la tastatură. Pentru determinarea cmmdc
al elementelor vectorului, vom scrie o funcţie care calculează şi returnează cmmdc a două numere
întregi a şi b, şi pe care o vom folosi într-o buclă pentru elementele vectorului.
#include<stdio.h>
void citire(int& n, int x[]);
int cmmdc(int a,int b);
void main()
{
int i,n,v[50],cm;
citire(n,v);
cm=cmmdc(v[0],v[1]);
for(i=2;i<n;i++)
cm=cmmdc(cm,v[i]);
printf("cmmdc=%d\n",cm);
}
void citire(int& n, int x[])
{
Informatică pentru clasa a X-a 15

int i;
printf("n="); scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("el[%d]=",i);
scanf("%d",&x[i]);
}
}
int cmmdc(int a,int b)
{
while(a!=b)
if(a>b)
a-=b;
else
b-=a;
return a;
}

Exemplul 3: Se citesc două numere naturale m şi n, m<n. Să se tipărescă toate numerele


palindromice aflate în intervalul [m,n]. Un număr este palindromic dacă citit de la dreapta la stânga şi
de la stânga la dreapta reprezintă aceeaşi valoare.
Pentru a verifica dacă un număr natural x este palindrom, vom scrie o funcţie care vareturna 1
în caz afirmativ şi 0 în caz contrar. Funcţia va construi numărul cu cifrele în ordine inversă şi îl va
compara cu parametrul efectiv primit.
#include<stdio.h>
int palindrom(int x)
{
int inv=0,t=x;
while(x)
{
inv=inv*10+x%10;
x/=10;
}
return inv==t;
}
void main()
{
int m,n,i;
printf("m,n="); scanf("%d %d",&m,&n);
printf("\t Numere palindromice din intervalul [%d,%d]:\n",m,n);
for(i=m;i<=n;i++)
if(palindrom(i)) printf("%d ",i);
printf("\n");
}

Exemplul 4: Să se scrie o funcţie care citeşte o matrice cu elemente numere întregi dintr-un fişier text
cu numele f.in. Pe prima linie a fişierului se găsesc două valori separate prin spaţii care reprezintă
numărul de linii şi, respectiv, de coloane (m şi n). Următoarele m linii ale fişierului conţin, în ordine,
elementele aflate pe fiecare linie a matricei. Matricea astfel obţinută va fi tipărită în main().
Funcţia de citire a matricii va primi ca parametri de intrare referinţe la variabilele m şi n şi
adresa tabloului citit, care are cel mult 10 linii şi 10 coloane.
#include<stdio.h>
void citire(int& m,int& n,int a[10][10])
{
int i,j;
FILE *f;
f=fopen("f.in","rt");
fscanf(f,"%d %d",&m,&n);
for(i=0;i<m;i++)
for(j=0;j<n;j++)
fscanf(f,"%d",&a[i][j]);
fclose(f);
}
16 Informatică pentru clasa a X-a

void main()

{
int m,n,a[10][10],i,j;
citire(m,n,a);
printf("\n\t Matricea citita este:\n");
for(i=0;i<m;i++)
{
for(j=0;j<n;j++) printf("%4d",a[i][j]);
printf("\n");
}
}

4. Exerciţii şi teste grilă

1. Precizaţi care dintre următoarele linii de variabile întregi. Cum realizăm apelul funcţiei,
program reprezintă corect,dpdv sintactic, lista astfel încât la apel să dăm ca parametri
de parametri şi valoarea returnată de o funcţie variabilele întregi a,b şi c?
cu numele test, dacă cerem ca parametrii a) demo(int a,int b,int c);
formali să fie două variabile de tip întreg, x şi y, b) demo(int a;int b;int c);
şi o variabilă de tip real z, şi să returneze un c) demo(a,b,c);
rezultat întreg. d) demo(a;b;c);
a) int test(int x,y,float z) e) demo(int a,b,c);
b) int test(int x,y;float z)
c) int test(int x,int y,float z) 5. Scrieţi o funcţie D care primeşte ca
d) test(int x;int y;float z) int parametru un număr întreg a şi returnează
e) test(int x,int y,float z) int valoarea lui a+2.
a) int D(int a);{D=(a+2);}
2. Care dintre afirmaţiile de mai jos sunt b) int D(int a){D=a+2;}
adevărate? c) int D(int a);{return(a+2);}
a) la apelul unei funcţii, se produce d) int D(int a){return a+2;}
înlocuirea parametrilor formali cu e) nici una dintre variantele
parametrii actuali anteriare
b) tipul parametrilor daţi la apelul unei
funcţii trebuie să coincidă sau să fie 6. Care dintre afirmaţiile de mai jos sunt
compatibil cu tipul celor definiţi în adevărate?
antetul funcţiei a) parametrii definiţi în antetul unei funcţii
c) la apelul unei funcţii, se salvează pe se numesc actuali, iar cei care apar la
stivă adresa de revenire, precum şi apelul funcţiei se numesc formali
variabilele locale şi parametrii b) valoarea returnată de către o funcţie
modulului apelat poate fi transmisă ca parametru altei
d) orice funcţie trebuie să aibă funcţii
întotdeauna cel puţin un parametru c) variabilele de tip tablou nu se pot
e) nici una dintre afirmaţiile de mai sus transmite ca parametri funcţiilor
d) variabilele globale sunt cunoscute pe
3. Se consideră un vector v care poate memora tot parcursul programului în care au
maxim 25 de numere întregi, din care folosim fost declarate în toate modulele care
efectiv numai primele n elemente. Scrieţi urmează declaraţiei
antetul unei funcţii numită calcul, care primeşte e) corpul unei funcţii trebuie cuprins între
drept parametri vectorul v împreună cu numărul “{“ şi “}”, numai dacă este alcătuit din
său de elemente şi nu returnează nimic. cel puţin două instrucţiuni distincte
a) void calcul(int v[n],25)
b) void calcul(int v[25],int n) 7. Avantajele utilizării funcţiilor într-un program
c) void calcul(int v[],int n) sunt:
d) void calcul(int v[],n) a) se poate obţine o economisire a
e) void calcul(int v,int n) spaţiului de memorie rezervat
variabilelor folosite în cadrul
4. Considerăm o funcţie demo, de tip void programului
pentru care se definesc ca parametri trei
Informatică pentru clasa a X-a 17

b) o viteză mai mare în execuţia printf(“%6d”,m); // (3)


programului printf(“%6d %6d”,b,c); // (4)
c) posibilitatea de a executa de mai multe }
ori instrucţiunile cuprinse într-o funcţie Care dintre următoarele afirmaţii sunt
d) un program care conţine funcţii poate fi adevărate?
urmărit şi corectat mai uşor a) în cursul execuţiei primului apel al
e) nici unul dintre avantajele de mai sus funcţiei p, cel din linia (1), se vor afişa,
8. Deduceţi şirul de valori care se afişează în în ordine, valorile 14,9,14 (liniile (5) şi
urma execuţiei programului de mai jos: (6))
#include<stdio.h> b) cele două apeluri ale funcţiei p, din
int i,j,k; liniile (1) şi (2), sunt corecte şi
int test(int x,int y) echivalente (produc acelaşi rezultat în
{ orice situaţie)
return (x-y); c) linia (3) afişează valoarea 14
} d) linia (4) afişează valorile 9 şi 14
void calcul(int p,int q) e) la fiecare execuţie a funcţiei p, se
{ salvează pe stivă valoarea lui c
int u,v;
u=p-i; v=q+j; 11. Ce valori va afişa programul următor?
i=test(u,q); #include<stdio.h>
j=test(v,p); int n,m;
} void t(int n,int& m)
void main() {
{ n+=2; m--;
i=2; j=3; }
calcul(i,,j); void main()
printf(“%3d %3d”,i,j); {
calcul(j,i); n=2; m=5;
printf(“%3d %3d\n”,i,j); t(n,m);
} printf(“%d %d”,n,m);
a) 2 3 2 3 b) 3 2 3 2 n=10; m=20;
c) 2 3 –3 4 d) –3 4 4 –3 t(n,m);
e) –3 4 10 –3 printf(“%d %d”,n,m);
}
9. Câte erori va produce execţia programului a) 4 4 12 19 b) 4 5 12 20
următor? c) 2 4 10 19 d) 2 5 10 20
#include<stdio.h> e) 7 2 22 10
#include<math.h>
int calcul(int p,int q) 12. Precizaţi ce se va afişa în urma execuţiei
{ programului de mai jos:
return(sqrt(p*q)); #include<stdio.h>
} int v[2],i;
void main() void test(int x[],int k)
{ {
printf(“%d”,calcul(1,2)); x[k]=0;
} }
a) una b) două c) trei void main()
d) patru e) nici una {
v[0]=1; v[1]=2;
10. Considerăm programul următor: for(i=0;i<2;i++)
#include<stdio.h> {
int m; test(v,i);
void p(int& c) printf(“%2d%2d”,v[0],v[1]);
{ }
int b; }
b=3*m; c=b+2; m+=3; a) 0 2 0 0 b) 0 2 1 2
printf(“%6d”,m); // (5) c) 1 2 1 0 d) 1 2 1 2
printf(“%6d %6d\n”,b,c);//(6) e) 1 2 2 2
}
void main() 13. Fie programul:
{ #include<stdio.h>
m=3; p(m); // (1) int j,x;
p(3); // (2) int test(int,int);
18 Informatică pentru clasa a X-a

void main() {
{ f=10; s=sf(f);
x=2; printf(“%6d%6d”,s,f);
printf(“%d”,test(x,x+3);//(1) f=10; s=sf(10); s*=sf(f);
printf(“%d”,x); //(2) printf(“%6d%6d”,s,f);
} f=10; s=sf(f); s*=sf(10);
int test(int a,int b) printf(“%6d%6d”,s,f);
{ }
int x=a+b; a) 100,10,10000,10,10000,10
for(j=1;j<=x;j++) b) 100,0,100,0,100,0
if(j>a && j<b) return j; c) 0,0,0,0,0,-10
return 0; d) 100,0,10000,0,16960,0
} e) 100,0,0,0,10000,-10
Precizaţi care dintre afirmaţiile de mai jos sunt
adevărate: 16. Determinaţi valorile pe care le afişează
a) instrucţiunea (1) afişează valoarea 3 programul de mai jos:
b) instrucţiunea (2) afişează valoarea 7 #include<stdio.h>
c) funcţia test returnează 0, indiferent int x,y;
care ar fi parametrii daţi la apel int T(int m,int n)
d) corpul funcţiei test este eronat, {
deoarece conţine două instrucţiuni m=n+x; n+=1;
return return(n+y+m);
e) programul este eronat, deoarece }
variabila x a fost declarată de două ori void main()
{
14. Deduceţi ce valori va afişa, în ordine, y=10; x=12;
programul următor? printf(“%3d”,T(x,y));
#include<stdio.h> printf(“%3d%3d”,x,y);
void calcul(int *u,int *v) }
{ a) 43,22,10 b) 43,12,10
int j; c) 47,10,12 d) 44,22,11
for(j=0;j<3;j++) e) 44,12,11
{
v[j]=1-u[j]; u[j]=0; 17. Fie programul:
} #include<stdio.h>
} void F(……..)
void main() {
{ a+=2; b--;
int u[5],v[5],j; c=a+b;
for(j=0;j<3;j++) }
{ void main()
u[j]=j%2; v[j]=0; {
} int x,y,z;
calcul(u,v); x=2; y=4;
for(j=0;j<3;j++) F(x,y,z);
printf(“%d “,u[j]); printf(“%3d%3d%3d”,x,y,z);
for(j=0;j<3;j++) }
printf(“%d “,v[j]); Funcţia F primeşte ca parametri trei numere
} întregi a,b,c. Cum trebuie scris antetul complet
a) 1 0 1 0 0 0 b) 0 0 0 1 0 1 al funcţiei, astfel încât programul să afişeze, în
c) 1 0 1 0 1 0 d) 0 1 0 1 0 1 ordine, valorile 4 4 7?
e) 0 1 0 0 0 0 a) void F(int a,int b,int c)
b) void F(int a,int& b,int& c)
15. Deduceţi şirul de valori care se afişează în c) void F(int& a,int b,int& c)
urma execuţiei programului următor. d) void F(int& a,int b,int c)
#include<stdio.h> e) void F(int& a,int& b,int& c)
int s,f;
int sf(int a) 18. Ce valoare trebuie citită în variabila m astfel
{ încât programul următor să afişeze valoarea 4?
f-=a; #include<stdio.h>
return(a*a); void F(int& nr,int x)
} {
void main() do{
Informatică pentru clasa a X-a 19

x=x/2; înaintea lui main. Cum poate arăta acest


nr++; prototip?
}while(x>0); a) void calcul(int v,float x,
} int a);
void main() b) void calcul(int v[],
{ float x,int a);
int m,n; c) void calcul(int,float,int);
scanf(“%d”,&m); d) void calcul(int [],
n=0; float,int);
F(n,m); e) void calcul(int*,float,int);
printf(“%d”,n);
} 21. Deduceţi ce valori va afişa, în ordine,
a) 12 b) 13 c) 14 d) 15 e) 16 programul următor?
#include<stdio.h>
19. Precizaţi care dintre funcţiile de mai jos int u[5],v[5],w[5],t[5],j;
returnează 1 dacă numărul x este prim, 0 în void calcul(int* w,int* t)
caz contrar. {
a) int p(int x) for(j=0;j<3;j++)
{ {
int j,ok; w[j]=0;
for(j=2;j<x;j++) t[j]=v[j]-u[j];
if(x%j==0) ok=0; }
else ok=1; }
return ok; void main()
} {
b) int p(int x) for(j=0;j<3;j++)
{ {
int j; u[j]=2*j-1;
for(j=2;j<x;j++) v[j]=3*j-2;
if(x%j==0) return 0; w[j]=u[j]+v[j];
else return 1; t[j]=0;
} }
c) int p(int x) calcul(w,t);
{ for(j=0;j<3;j++)
int j,ok=1; printf(“%d “,w[j]);
for(j=2;j<x;j++) for(j=0;j<3;j++)
if(x%j==0) ok=0; printf(“%d “,t[j]);
return ok; }
} a) –3 2 7 –1 0 1 b) 0 0 0 –1 0 1
d) int p(int x) c) –3 2 7 0 0 0 c) –1 0 1 –3 2 7
{ d) 0 0 0 –3 2 7
int j;
return 1; 22. Ce valori se vor afişa în urma execuţiei
for(j=2;j<x;j++) programului următor:
if(x%j==0) return 0; #include<stdio.h>
} int f1(int *p)
{
20. Se consideră următoarea structură de *p=43;
program: return 1;
#include<stdio.h> }
void main() int *f2(int *q)
{ {
int v[20],j,n; int m=f1(q);
………….. return &m;
calcul(v,2.5,10); }
} void main()
void calcul(int v[],float x,int a) {
{ int x,y;
…………….. y=*f2(&x);
} printf(“\n%d %d”,x,y);
Funcţia calcul fiind scrisă după funcţia main, }
are nevoie de un prototip care trebuie plasat a) programul este eronat
b) 0 şi 0 c) 43 şi 43
20 Informatică pentru clasa a X-a

d) 1 şi 43 e) 43 şi 1 int f(int a=5,int b=2)


{
23. Considerăm următorul program: return –-a+2*b++;
#include<alloc.h> }
#include<stdio.h> void main()
int *f(int *a,int *b) {
{ printf(“%d”,f());
return (*a>=*b)? a:b; }
} a) 7 b) 8 c) 9 d) 10 e) 11
void main()
{ 26. Se consideră programul următor:
int *x,*y; #include<stdio.h>
x=(int*)malloc(sizeof(int));//(1) int f(int x,int y=5)
y=(int*)malloc(sizeof(int));//(2) {
scanf(”%d %d”,x,y); //(3) static int m=3;
printf(„%d”,*f(x,y)); //(4) m+=x;
free(x); //(5) return m>=y? m : -1;
free(y); //(6) }
} void main()
În timpul execuţiei programului sunt posibile {
următoarele situaţii: int a,b,c;
a) funcţia nu returnează corect valoarea a=f(4); b=f(1,7); c=f();
dorită, deoarece tipul valorii returnate }
nu este cel definit în antetul funcţiei Care dintre cele trei apeluri realizate în main
b) în locul liniilor (1) şi (2), pentru alocarea sunt corecte?
dinamică a memoriei putem scrie a) numai primele două apeluri sunt
x=y=(int*)malloc(sizeof(int)); corecte iar valorile variabilelor sunt a=7
c) în locul liniilor (5) şi (6), pentru şi b=8
eliberarea memoriei alocate putem b) numai primele două apeluri sunt
scrie free(x,y); corecte iar valorile variabilelor sunt a=7
d) instrucţiunea scanf din linia (3) este şi b=-1
eronată; corect era scanf(“%d”,&x, c) numai al doilea apel este corect şi se
&y);, deoarece numele variabilelor obţine b=8
citite trebuie precedat în scanf de d) numai al doilea apel este corect şi se
operatorul ”&” obţine b=-1
e) programul este în totalitate corect, el e) toate cele trei apeluri sunt corecte
afişând astfel maximul a două numere
întregi definite cu ajutorul pointerilor x 27. Fie funcţia F de mai jos:
şi y int* F(int* a,int* b,int c)
{
24. Deduceţi ce numere va afişa programul int d=(*a+*b+c)/3;
următor: return &d;
#include<stdio.h> }
int& f(int a,int b,int& c) Presupunem că sunt declarate următoarele
{ variabile:
c=a+b; int *x,*y,z,*m,n;
return c; Care dintre secvenţele de program de mai jos
} nu afişează corect valoarea returnată de către
void main() funcţia F?
{ a) scanf(“%d %d %d”,x,y,&z);
int x=2,y=3,z=11; printf(“\n%d”,*F(x,y,z));
printf(“%d”,f(x,y,z)); b) *m=*F(x,y,z);
printf(“%d”,z); printf(“%d”,*m);
f(x,y,z)=9; c) n=*F(x,y,z);
printf(“%d”,z); printf(“%d”,n);
} d) printf(“%d”,*F(2,3,4));
a) 5,11,5 b) 5,11,9 c) 5,11,11
d) 5,11,9 e) 5,5,9 28. Fie funcţia:
void afis1(int v[])
{
25. Ce valoare se va afişa în urma execuţiei int j=0;
programului următor? while(j<4)
#include<stdio.h> printf(“%d “,v[j++]);
Informatică pentru clasa a X-a 21

} }
Funcţiile afis2 şi afis3 de mai jos sunt corecte void B(int* p)
şi echivalente cu funcţia dată afis1? (adică {
afişează acelaşi şir de valori) int y=4;
void afis2(int v[4]) y=*A(&y,p);
{ }
int j=0; void main()
do {
printf(“%d “,*(v+j++); int y=9,x;
while(j<4); B(&x);
} printf(“%d %d”,x,y);
void afis3(int v[4]) }
{ a) programul este eronat
int j,*x[4]; b) 0 şi 9 c) 5 şi 0 d) 5 şi 9 e) 0 şi 0
for(j=0;j<4;j++)
{ 31. Fie funcţiile f1 şi f2 având prototipurile:
x[j]=&v[j]; void f1(int (*p)[8]);
printf(“%d “,*x[j]); void f2(int p[4][8]);
} Analizaţi corectitudinea apelurilor celor două
} funcţii în următorul program:
a) ambele funcţii afis2 şi afis3 conţin erori void main()
b) ambele funcţii afis2 şi afis3 sunt {
corecte şi echivalente cu afis1 int m[4][8],(*q)[8],*r[8];
c) ambele funcţii afis2 şi afis3 sunt f1(m);f2(m);f1(q);f2(q);
corecte, dar nici una dintre ele nu este f1(r);f2(r);
echivalentă cu afis1 }
d) funcţia afis2 este corectă şi a) numai primele patru apeluri sunt
echivalentă cu afis1, iar afis3 conţine corecte
erori b) numai primele două apeluri sunt
e) funcţia afis3 este corectă şi corecte
echivalentă cu afis1, iar afis2 conţine c) numai primele două apeluri sunt greşite
erori d) toate apelurile sunt greşite
e) toate apelurile sunt corecte
29. Care va fi valoarea lui n în urma execuţiei
programului următor? 32. Ce reprezintă declaraţia următoare:
#include<stdio.h> int *(*f)(int *);
int* intreg(float* p) a) este greşită sintactic
{ b) funcţie care primeşte un argument
return (int*)p; pointer la întreg şi întoarce pointer la
} întreg
void main() c) pointer către întreg
{ d) pointer către funcţie care are argument
int n; float x=-3.56; pointer către întreg şi întoarce pointer
n=x; către întreg
n=*intreg(&x); e) o altă semnificaţie decât cele
} precedente
a) n va avea valoarea –3
b) n va avea valoarea –4 33. Fie programul:
c) n nu va avea în nici un caz valorile de #include<stdio.h>
la a) sau b) int plus(int x)
d) n va avea valoarea –3.56 {
e) programul conţine erori de sintaxă return x++;
}
30. Ce valori se vor afişa în urma execuţiei int minus(int x)
programului următor? {
#include<stdio.h> return –x;
int q; }
int* A(int* m,int* n) void test(int p,int q,
{ int (*F1)(int),int (*F2)(int))
*n=*m+1; {
*m=*n+1; if(p && q)
q=*m-1; printf(“%d”,F1(p));
return &q; else printf(“%d”,F2(q));
22 Informatică pentru clasa a X-a

} printf(“%f”,f(3,2.0,8.0,
void main() -1.0));
{ }
int a=2,b=5; a) *(double *)(&n+1+j);
test(a,b,plus,minus); b) *((double *)(&n+1)+j);
test(3,0,plus,minus); c) *(double *)(&n+(1+j));
test(a,b,plus(a),mins(b)); d) *(double *)(&(n+1)+j);
test(3,0,plus(3),minus(0)); e) o altă construcţie decât cele anterioare
}
Ce se poate spune despre apelurile funcţiei test 36. Precizaţi ce va afişa programul de mai jos:
realizate din main? #include<stdio.h>
a) primele două apeluri sunt corecte, char &f(char *p,int n)
determinând afişarea valorilor 2, {
respectiv –1, iar ultimele două sunt return *(p+n);
eronate }
b) primele două apeluri sunt corecte, void main()
determinând afişarea valorilor 3, {
respectiv –1, iar ultimele două sunt char *s=”abc”;
eronate f(s,1)=’a’;
c) ultimele două apeluri sunt corecte, printf(„%s”,s);
determinând afişarea valorilor 2, }
respectiv –1, iar primele două sunt a) programul nu va afişa nimic pentru că
eronate este greşit
d) ultimele două apeluri sunt corecte, b) “aac” c) “abc” d) “aba” e) “aabc”
determinând afişarea valorilor 3,
respectiv –1, iar primele două sunt 37. Care dintre apelurile de funcţie de mai jos
eronate va schimba valoarea lui n?
e) toate cele patru apeluri sunt eronate #include<stdio.h>
int *f(int& n)
34. Ce valoare se va afişa în urma execuţiei {
programului următor? return &n;
#include<stdio.h> }
int f(int n,…) void main()
{ {
int *a=&n+1, *b=a+2; int n=7;
return n*(*a+*b); f(n)=2; // (1)
} *f(n)=3; // (2)
void main() }
{ a) ambele b) nici una c) 1
printf(“%d”,f(3,2,11,5,8)); d) 2 e) antetul funcţiei este greşit
}
a) 21 b) 39 c) 18 d) 30 38. Analizaţi programul de mai jos şi precizaţi
e) antetul funcţiei este greşit care dintre afirmaţiile date sunt false.
#include<stdio.h>
35. Ce trebuie pus în locul secvenţei XXX astfel int &f1(int& x)
încât programul de mai jos să furnizeze {
minimul unui număr variabil de argumente, return x=1;
precizat la apel prin valoarea lui n? }
#include<stdio.h> int *f2(int *p)
double f(int n,…) {
{ int a=2;
double min=*(double *)(&n+1); f1(a); *p=3;
for(int j=1;j<n;j++) return p;
if(XXX<min) min=XXX; }
return min; void main()
} {
void main() int b=4;
{ f1(b); f1(b)=5;
int n=3; f2(&b);
float a=2,b=8,c=-1; *f2(&b)=6;
printf(“%f”,f(n,a,b,c)); }
printf(“%f”,f(n,2.0,8.0, a) în urma execuţiei f1(b), obţinem b=4
-1.0)); b) în urma execuţiei f1(b)=5, obţinem b=5
Informatică pentru clasa a X-a 23

c) în urma execuţiei f2(&b), obţinem b=3 int f(int a)


d) în urma execuţiei *f2(&b), obţinem b=6 {
e) valoarea variabilei locale a din funcţia if(a>=’A’&&a<=’Z’)
f2 nu poate fi modificată prin nici un return c+’a’-‘A’
mecanism în funcţia main else
return c;
39. Precizaţi valoarea variabilei n rezultată în }
urma execuţiei programului: Ce realizează funcţia ?
#include<stdio.h> a) converteşte litera mică a în litera mare A
int f(char a[2]) b) converteşte litera mare A în litera mică a
{ c) converteşte literele mici în litere mari
int j=0; d) converteşte literele mari în litere mici
while(a[j++]); return j;
} 43. Ce se afişează pe ecranul monitorului ,
void main() după execuţia următorului program ?
{ #include <stdio.h>
int n=f(“abcdefgh”); int i,j,k;
} void f1(int x,int *y)
a) 0 b) 2 c) 9 {
d) corpul funcţiei conţine o buclă infinită *y=x+2;
e) programul are erori de sintaxă --x;
}
40. Fie programul : void f2(int u,int *v)
#include <stdio.h> {
void fct(int a,int *b) int m,n;
{ m=u-j;
a+=3; *b+=a; f1(m,v);
} f1(*v+i,&n);
void main() *v+=n;
{ }
int c, d; void main(void)
c=4; d=5; {
fct(c,&d); i=4; j=3; k=5;
printf(“%d%d”,c,d); f2(i,&j);
fct(c,&d); printf(“%d%d%d”,i,j,k);
printf(“%d%d”,c,d); f2(j,&k);
} printf(“%d%d%d”,i,j,k);
Ce se afişează pe ecranul monitorului în urma f2(k,&k);
execuţiei programului ? printf(“%d%d%d”,i,j,k);
a) 4545 b) 412412 }
c) 712719 d) 412419 a) 4 3 5
4 3 10
41. Se dă funcţia : 4 3 20
int fct(int n) b) 4 3 5
{ 4 6 10
int d=2; 4 12 20
int g=(int) sqrt(n); c) 4 12 5
int f=1; 4 12 10
while (d<=g&&f) 4 12 6
{ d) 4 3 5
f=f&&n%d; 4 7 10
d=d+1 4 11 12
}
return f; 44. Presupunând că n este numărul de
} elemente ale unui vector de numere reale , să
Să se precizeze ce prelucrare se realizează în se specifice când funcţia de mai jos întoarce
această funcţie : valoarea 0 .
a) se determină factorii primi ai lui n int f(int n)
b) se determină numere prime până la √n {
c) se verifică dacă numărul n este prim int i;
d) se verifică dacă n se împarte exact la d for(i=1;i<n-1;i++)
if(x[i]==x[i+1]) return 0;
42. Fie funcţia : return 1;
24 Informatică pentru clasa a X-a

} }
a) dacă două elemente din vector sunt return x;
distincte }
b) dacă oricare două elemente din vector Pentru n=121 , f capătă valoarea :
sunt egale a) 121 b) 4 c) 3 d) 121000
c) dacă primele două elemente din vector
sunt egale 49. Se consideră funcţia următoare :
d) dacă două elemente consecutive sunt int f(int n)
egale , fără a considera primul element { int x=0; int c=0;
int a=n;
45. Spuneţi ce realizează următoarul while (n)
subprogram : { c=n%10;
void fct(char s[]) x=x*10+c;
{ n=n/10;
int i,j,t; }
for(i=0,j=strlen(s)-1;i<j; if(x==a) return 1;
i++,j--) else return 0;
{ }
t=s[i];s[i]=s[j];s[j]=t; Valoarea f=1 arată că :
} a) n este număr prim
} b) n este număr par
a) inversează un şir de caractere c) n este palindrom
b) aduce caracterul terminator al şirului pe d) x este divizor al lui n
prima poziţie invalidându-l
50. Se consideră funcţia următoare :
c) şterge un şir de caractere
int f(void)
d) introduce caractere aleatoare în şir
{ int i;
printf(“%d”,i);
46. Ce realizeaza urmatoarea procedura : }
void f(FILE *p)
{ Valoarea afişată de funcţie este :
int c; a) 0 b) 1 c) 32737
while((c=getc(p))!=EOF) d) nu se poate preciza valoarea afişată
putc(c,stdout);
51. Se consideră subprogramul :
} int f(int a,int b)
a) copie , caracter cu caracter , un fisier in { int r=a%b;
alt fisier while (r>0)
b) copie , caracter cu caracter , un fisier {a=b;b=r;r=a%b;}
pe ecranul monitorului return b;
c) copie , caracter cu caracter , intrarea }
standard la iesirea standard Pentru a=45 si b=27 , valoarea lui f este:
d) copie intrarea standard intr-un fisier a) 3 b) 9 c) 72 d) 135
47. Se considera urmatoarea functie : 52. Să se precizeze care sunt valorile care vor
int f(int n) fi tipărite de programul de mai jos :
{ int i,j;
scanf(“%d”,&n); void p(int j)
f=1; { j++;
printf(“%d”,n); i=j+1;
} }
Pentru n=100 pe ecranul monitorului se va void main(void)
afisa : { i=2; j=4;
a) 100 b) 1 p(i);
c) programul are erori de sintaxa printf(“%d %d”,i,j);
d) programul are erori de semantica }
a) 1 6 b) 1 4 c) 4 6 d) 4 4
48. Se consideră funcţia demai jos :
int f(int n)
53. Se consideră următoarea funcţie :
{
float x[10];
int x=0; int c=0;
int f(int n)
while(n)
{ int i,k=0;
{ c=n%10;
for(i=1;i<n;i++)
x=x*10+c;
if(x[i]==x[i-1]) k=k+1;
n=n/10;
if(k==0) return 0;
Informatică pentru clasa a X-a 25

else return k; x++; y++;


} printf(“%d-%d\n”,x,y);
Dacă x=(5, 6, 6, 6, 3, 4, 4, 9) valoarea returnată }
pentru f este : void main()
a) 3 b) 2 c) 7 d) 5 { func();
func();
54. Care este implementarea corecta a functiei }
toupper ? Ce va afişa codul de mai sus când va fi
I) int tomare(char c) executat ?
{ a) 1—0 1—0
return(c-‘a’+’A’); b) 1—1 1—1
} c) 1—1 1—2
II) int tomare(char c) d) 1—1 2—2
{
if(c>=’a’&&c<=’z’) 58. Funcţia:
return(c-‘a’+’A’); void functie(int n,int (*a)[10])
return c; { int i,j;
} for(i=0;i<n;i++)
III) int tomare(char c) for(j=0;j<n;j++)
{ scanf(“%2d”,&a[i][j]);
if(c>=’a’&&c<=’Z’) }
return(c+’a’-‘A’); realizează :
return c; a) citirea unei matrici
} b) citirea unui vector
a) I b) II c) III d) II si III c) citirea a doi vectori
d) citirea a două şiruri de caractere
55. Fie funcţia f care primeşte la intrare un
vector Vn prezentată mai jos : 59. Funcţia:
int f(int *v,int n) void functie(int (*a)[10])
{ int i; { int i,j;
for(i=0;i<n;i++) double temp;
{ j=0; for(i=0;i<n-1;i++)
while(j<n) for(j=i+1;j<n;j++)
{ if(v[i]==v[j]) return 1; { temp=a[i][j];
else return 0; a[i][j]=a[j][i];
j++; a[j][i]=temp;
} }
} }
} realizează :
a) funcţia returnează valoarea 1 dacă a) transpusa unei matrici
există elemente duplicate în vectorul v b) inversarea unei matrici
b) numără apariţiile lui n în vector c) inversarea a n linii
c) realizează o altă operaţie d) inversarea a n coloane
d) ordonează vectorul
60. Funcţia C :
56. Ce face următorul program ? int functie(double x,int n,int *a)
int function p3(int x) { int k,i;
{ return x*x*x; k=0;
} for(i=0;i<n;i++)
void main(void) if(a[i]<x) k++;
{ int x,y; return k;
scanf(“%d%d”,&x,&y); }
printf(“%d”,p3(x+y)); returnează :
} a) cele k elemente mai mici ca x dintr-un
a) este eronată vector a
b) calculează x3 b) cele k-1 elemente mai mici ca x dintr-
c) calculează (x+y)3 un vector a
d) calculează y3 c) cele k elemente mai mici egale ca x
dintr-un vector a
57. Se dă codul : d) cele k elemente mai mari ca x dintr-un
void func() vector a
{ int x=0;
static int y=0; 61. Ce realizează funcţia de mai jos ?
26 Informatică pentru clasa a X-a

int f(int tab[],int n,int a) }


{ int j,s,i; vor afişa următoarele rezultate:
i=0; s=n-1; a) a=10 b=20 c=30
while(i<=s) a=11 b=21 c=31
{ j=(i+s)/2; b) a=10 b=20 c=30
if(tab[j]==a) s=j-1; a=10 b=20 c=30
else i=j+1; c) a=11 b=21 c=31
} a=10 b=20 c=30
return –1; d) nici una din variantele 1-3
}
a) realizează o sortare a elementelor 63. Următoarele linii de cod :
tabloului tab #include<stdio.h>
b) realizează o parcurgere a tabloului tab void incr(int *x,int *y,int *z)
şi returnează elementul de la jumătatea { *x=*x+1;
acestuia *y=*y+1;
c) caută în tabloul tab un element cu *z=*z+1;
valoarea egală cu a ; returnează }
indicele elementului respectiv void main(void)
d) funcţia nu realizează nimic { int a=10, b=20, c=30;
printf(“a=%d b=%d c=%d”,a,b,c);
62. Următoarele linii de cod : incr(&a,&b,&c)
#include<stdio.h> printf(“a=%d b=%d c=%d”,a,b,c);
void incr(int x,int y,int z) }
{ x=x+1; vor afişa următoarele rezultate:
y=y+1; a) a=10 b=20 c=30
z=z+1; a=11 b=21 c=31
} b) a=10 b=20 c=30
void main(void) a=10 b=20 c=30
{ int a=10, b=20, c=30; c) a=11 b=21 c=31
printf(“a=%d b=%d c=%d”,a,b,c); a=10 b=20 c=30
incr(a,b,c) d) nici una din variantele 1-3
printf(“a=%d b=%d c=%d”,a,b,c);

Potrebbero piacerti anche