Sunteți pe pagina 1din 14

8.

Subprograme

8.1 Generaliti
Conform teoriei programrii, subprogramele snt clasificate n: - funcii, care returneaz un singur rezultat prin numele funciei i oricte prin parametri de ieire; - proceduri, care returneaz oricte rezultate, prin intermediul parametrilor de ieire. Un program C este un ansamblu de funcii care realizeaz activiti bine definite. Exist o funcie, numit main(), care este apelat la lansarea n execuie a programului. Subprogramele C snt, n mod nativ, funcii. Pot fi construite subprograme care nu returneaz nici un rezultat prin numele lor, comportndu-se ca o procedur (conform definiiei din teorie). Sistemele C au colecii de biblioteci care conin funcii standard (A). Textul surs al unui program C poate fi partiionat n mai multe fiiere. Fiecare fiier const dintr-un set de funcii i declaraii globale. Fiierele care constituie partiia pot fi compilate i, eventual, testate separat, dar numai unul va conine funcia main().

8.2 Construirea i apelul subprogramelor


Funciile C snt formate din antet i un corp. Antetul are forma:
tip nume([lista-parametri-formali])

unde: tip poate fi un tip simplu de dat. Dac lipsete, este considerat tipul implicit (int pentru unele compilatoare, void pentru altele);

tiina nvrii unui limbaj de programare. Teorie

nume este un identificator care reprezint numele funciei; lista-parametrilor-formali conine parametrii formali sub forma:
[tip1 identificator1[,tip2 identificator[,tip3 identificator ]]]

Parametrii snt separai prin virgul. La limit, lista poate fi vid. Pentru fiecare parametru trebuie specificat tipul, chiar dac mai muli parametri snt de acelai tip. Nu este posibil definirea de liste de parametri cu acelai tip, ca n Pascal. Pentru funciile care nu ntorc o valoare prin numele lor, tipul funciei va fi void sau va fi omis. Corpul este o instruciune compus: conine declaraiile locale i instruciunile executabile care implementeaz algoritmul. Corpul funciei se execut pn la executarea ultimei instruciuni sau pn la executarea instruciunii return. Forma ei general este:
return(expresie); return expresie; return;

sau sau

Prima i a doua form snt folosite n cazul funciilor care returneaz o valoarea prin numele lor. Prin executarea acestei instruciuni se evalueaz expresia, valoarea sa este atribuit funciei i se ncheie execuia funciei. A treia form este folosit n cazul funciilor care nu returneaz nici o valoare prin numele lor (poate chiar s lipseasc). Dac este prezent, efectul ei este ncheierea execuiei funciei. Tipul expresiei din instruciunea return trebuie s coincid cu tipul funciei. n limbajul C nu este admis imbricarea (D) (definirea unui subprogram n cadrul altui subprogram) i nu snt permise salturi cu instruciunea goto n afara subprogramului. Declararea unui subprogram apare, n cadrul fiierului surs, naintea primului apel (A). Exist cazuri particulare n care fie funciile se apeleaz unele pe altele (de exemplu, cazul recursivitii mutuale), fie definiia nu se afl n fiierul surs. Pentru a oferi compilatorului posibilitatea s efectueze verificarea validitii apelurilor, snt prevzute declaraii ale subprogramelor fr definire. Aceste declaraii se numesc prototipuri i apar n afara oricrui corp de funcie. Sintaxa general este:
tip nume ([lista-parametri-formali]);

Prototipul este de fapt un antet de funcie dup care se scrie caracterul ; (punct i virgul). Numele parametrilor pot lipsi, fiind suficient specificarea tipurilor lor. Folosirea prototipurilor este asemntoare cu utilizarea clauzei forward din Pascal. Prototipul trebuie inserat n program naintea primului apel al funciei. Domeniul de valabilitate a declaraiei unui subprogram este limitat la partea care urmeaz declaraiei din fiierul surs.

Subprograme

Prototipurile funciilor standard se afl n fiiere header (cu extensia .h). Utilizarea unei funcii din bibliotec impune includerea fiierului asociat, cu directiva #include. Fiind funcii, subprogramele C se apeleaz ca operanzi n expresii, prin numele funciei urmate de lista parametrilor reali. Expresia care conine apelul poate la limit s conin un singur operand i chiar s fie o instruciune de tip expresie (vezi capitolul Operatori i expresii). n aceste cazuri valoarea returnat de funcie se pierde, nefiind folosit n nici un fel. Exemple: S se scrie o funcie care calculeaz cel mai mare divizor comun dintre dou numere ntregi nenule, utiliznd algoritmul lui Euclid i un apelant pentru testare.
#include <stdio.h> /*definirea functiei cmmdc*/ int cmmdc(int a, int b) { int r,d=a,i=b; do {r=d%i; d=i; i=r;} while(r<>0); return i;} void main() { int n1,n2; printf("Numerele pentru care se va calcula cmmdc:"); scanf("%d%d",&n1,&n2); if(n1&&n2) printf("\ncmmdc=%d",cmmdc(n1,n2)); else printf("Numerele nu snt nenule!");}

Acelai exemplu folosind prototip pentru funcia cmmdc:


#include <stdio.h> /* prototipul functiei cmmdc*/ int cmmdc(int, int); void main() { int n1,n2; printf("Numerele pentru care se va calcula cmmdc:"); scanf("%d%d",&n1,&n2); if(n1&&n2) printf("\ncmmdc=%d",cmmdc(n1,n2)); else printf("Numerele nu snt nenule! ");} /*definirea functiei cmmdc*/ int cmmdc(int a, int b) { int r,d=a,i=b; do {r=d%i; d=i; i=r;} while(r<>0); return i;}

tiina nvrii unui limbaj de programare. Teorie

8.3 Transferul datelor ntre apelant i apelat


n practica programrii, s-au conturat dou posibiliti de transfer al datelor ntre apelant i apelat: prin parametri i prin variabile globale. Prin utilizarea variabilelor globale nu se face un transfer propriu-zis, ci se folosesc n comun anumite zone de memorie.

8.3.1 Transferul prin parametri


Principial, transferul se poate face prin valoare sau prin adres. n limbajul C este implementat numai transferul prin valoare (valoarea parametrului real este copiat n stiv, iar subprogramul lucreaz numai cu aceast copie). Operaiile efectuate asupra unui parametru formal scalar (care nu este masiv) nu modific, la ieirea din subprogram, parametrul real corespunztor. Transferul valorii este nsoit de eventuale conversii de tip, realizate pe baza informaiilor de care dispune compilatorul despre subprogram. Dac prototipul precede apelul subprogramului i nu exist o sublist variabil de parametri, conversiile se fac similar atribuirilor. Exemplu:
tip_returnat nume(tip_parametru p);

p este transferat prin valoare

Folosind transferul prin valoare se pot transmite numai parametri de intrare n subprogram. Pentru a putea folosi parametri de ieire trebuie simulat transferul prin adres. n acest scop, se vor efectua explicit operaiile care se fac automat la transferul prin adres din alte limbaje: se transmite ca parametru adres parametrului real, iar n subprogram se lucreaz cu indirectare. Exemplu:
tip_returnat nume(tip_parametru *p);

p este transferat prin valoare,

el este adresa parametrului real. Pentru parametrii de tip masiv, simularea transferului prin adres se face n mod implicit, datorit modului de construire a masivelor n C: numele masivului este un pointer. La apel, n stiv se va transfera adresa masivului, iar referirea elementelor se face automat prin calcul de adrese (vezi capitolul Pointeri). Urmtoarele prototipuri snt echivalente:
tip_returnat nume1(float v[], int n); tip_returnat nume2(float *v, int n);

Subprograme

Exemple: 1. a) S se calculeze produsul scalar dintre doi vectori. rezultatul se ntoarce prin numele funciei:
float ps(float x[], float y[], int n) { int i,prod=0; for(i=0;i<n;prod+=x[i]*y[i++]); return prod;}

Apelul se realizeaz astfel:


float a[30],b[30]; int dimensiune; printf("Produsul scalar al vectorilor a si b este:%f", ps(a,b,dimensiune));

b)

rezultatul se ntoarce prin parametru de ieire:


void ps(float x[], float y[], int n, float *prod) { int i; *prod=0; for(i=0;i<n;(*prod)+=x[i]*y[i++]);}

Apelul se realizeaz astfel:


float a[30],b[30],produs_scalar; int dimensiune; ps(a,b,dimensiune,&produs_scalar); printf("Produsul scalar al vectorilor a si b este:%f", produs_scalar);

2. S se calculeze elementul maxim dintr-un vector i poziiile tuturor apariiilor acestuia (v, n snt parametri de intrare; max, nr_ap, poz snt parametri de ieire).
void maxim(float v[],int n,float *max,int poz[]) { int i; for(*max=v[0],i=1;i<n;i++) if(*max<v[i]) {*nr_ap=1;poz[0]=i; max=v[i];} else if(*max==v[i])poz[*nr_ap++]=i;} *nr_ap,int

Apelul se realizeaz astfel:


float a[30],el_max; int dimensiune,nr_aparitii,pozitii[30]; maxim(a,dimensiune,&max,&nr_aparitii,pozitii);

tiina nvrii unui limbaj de programare. Teorie

Antetul subprogramului este echivalent cu construcia


void maxim(float *v, int n, float *max, int *nr_ap, int *poz)

pentru care corpul subprogramului este acelai. 3. S se calculeze produsul a dou matrice.
void produs(float a[][10],float b[][20], float c[][20],int m, int n,int p) { int i,j,k; for(i=0;i<m;i++) for(j=0;j<p;j++) for(c[i][j]=0,k=0;k<n;k++)c[i][j]+=a[i][k]*b[k][j];}

Observaie: Dei un tablou nu poate fi returnat ca tip masiv prin numele unei funcii, se pot scrie funcii care returneaz prin nume un tablou ca pointer deorece numele tabloului este echivalent n C cu adresa sa (pointer la nceputul masivului). Unui astfel de masiv i se aloc memorie n funcia care l calculeaz. Numele su este returnat ca pointer la primul element al tabloului. Exemple: 1. S se calculeze produsul dintre o matrice i un vector.
#include<malloc.h> float * prod(float a[][30], float v[],int m, int n) { float *p;int i,j; p=(float *)malloc(sizeof(float)*m); for(i=0;i<m;i++) for(p[i]=0,j=0;j<n;j++) p[i]+=a[i][j]*v[j]; return p;}

Apelul se realizeaz astfel: a)


float a[20][30], b[30], *c; int m,n; c=prod(a,b,m,n);

Cu vectorul c se lucreaz n modul obinuit: elementele se refer prin indexare (c[i], i=0..m ). b)
float a[20][30], b[30]; int m,n;

Subprograme

Se lucreaz cu vectorul prod(a,b,m,n) elementele sale se refer ca prod(a,b,m,n)[i], i=0..m. Atenie: la fiecare referire de element se apeleaz i se execut funcia, ceea ce duce la consum mare i inutil de resurse. Este preferabil prima variant. 2. S se realizeze un program C pentru ridicarea unei matrice la o putere. Pentru aceasta se folosesc dou funcii care returneaz, prin pointeri, produsul a dou matrice (nmulire), respectiv ridicarea unei matrice la o putere (putere).
#include<stdio.h> #include<conio.h> #include<alloc.h> float** inmultire(float **a,float **b,int n) { int i,j,k; float **c; c=(float **)malloc(n*sizeof(float *)); for(i=0;i<n;i++) *(c+i)=(float *)malloc(n*sizeof(float)); for(i=0;i<n;i++) for(j=0;j<n;j++) for(k=0,c[i][j]=0;k<n;c[i][j]+=a[i][k]*b[k++][j]); return c;} float** putere(float **a,int p,int n) { float **c,**ap;int l,m,i; ap=(float **)malloc(n*sizeof(float *)); for(i=0;i<n;i++) *(ap+i)=(float *)malloc(n*sizeof(float)); for(l=0;l<n;l++) for(m=0;m<n;ap[l][m]=a[l][m],m++); for(i=0;i<p-1;i++) {c=inmultire(a,ap,n); for(l=0;l<n;l++) for(m=0;m<n;ap[l][m]=c[l][m],m++);} return ap;} void main() { int i,j,p,n,l,m; float **a,**ap,f; clrscr(); printf("\n n="); scanf("%i",&n); a=(float **)malloc(n*sizeof(float *)); for(i=0;i<n;i++) *(a+i)=(float *)malloc(n*sizeof(float)); for(i=0;i<n;i++) for(j=0;j<n;j++) {scanf("%f ",&f); *(*(a+i)+j)=f;} scanf("%i",&p); ap=putere(a,p,n); for(i=0;i<n;i++) {for(j=0;j<n;j++) printf("%f ",*((*(ap+i)+j))); printf("\n");} getch();}

tiina nvrii unui limbaj de programare. Teorie

8.3.2 Transferul prin variabile globale


Variabilele globale se declar n afara funciilor, inclusiv n afara rdcinii. Ele pot fi referite din orice alte funcii, inclusiv din rdcin. De aceea, schimbul de valori ntre apelant i apelat se poate realiza prin intermediul lor. Variabilele declarate ntr-o funcie se numesc locale (din clasa automatic) i pot fi referite numai din funcia respectiv. Domeniul de valabilitate a unei variabile locale este blocul (funcia sau instruciunea compus) n care a fost definit. Exemplu:
#include <stdio.h> int a; float z(char b) { int b; } main() { int c; { /* instructiunea compusa r */ int d; } }

Domeniile de valabilitate a referirilor variabilelor declarate snt: b poate fi referit doar n funcia z; c poate fi referit doar n funcia main; d poate fi referit doar n instruciunea compus r; a este global i poate fi referit de oriunde.

8.4 Pointeri spre funcii


Limbajul C permite lucrul cu variabile de tip pointer, care conin adresa de nceput a unei funcii (a codului su executabil). Aceste variabile permit transferul adresei funciei asociate ca parametru, precum i apelul funciei prin intermediul pointer-ului su. Urmtoarea declaraie definete pointer_f ca pointer spre funcia cu rezultatul tip_returnat i parametrii parametri.
tip_returnat (*pointer_f)([parametri])

Observaie: Nu trebuie s se confunde un pointer la o funcie cu o funcie care are ca rezultat un pointer, cu sintaxa de forma tip_returnat *pointer_f([parametri]).

Subprograme

Adresa unei funcii se obine prin simpla specificare a identificatorului acesteia (fr specificarea parametrilor sau parantezelor) i poate fi atribuit unui pointer spre funcie cu rezultat i parametri compatibili. Pointerul poate fi folosit ulterior pentru apelul funciei sau transmis ca parametru real n apelul unui subprogram care conine, n lista parametrilor formali, un pointer la un prototip de funcie compatibil. Exemple: 1. S se aproximeze soluia unei ecuaii de forma f(x)=0 prin metoda biseciei.
#include<stdio.h> #include<conio.h> #include<math.h> /*prototipul functiei bisectie*/ void bisectie(float,float,float(*f)(float),float,long,int *);

*,float

/*prototipul functiei pentru care se aplica metoda bisectiei*/ float fct(float); /* functia principala*/ void main() { float a,b,eps,x; int cod;long n; float (*functie)(float); clrscr(); printf("Introduceti capetele intervalului:"); scanf("%f%f",&a,&b); printf("\nEroarea admisa:"); scanf("%f",&eps); printf("\nNumarul maxim de termeni construiti:"); scanf("%li",&n); functie=fct; bisectie(a,b,functie,eps,n,&cod,&x); if(!cod)printf("\nNu se poate calcula solutia aproximativa"); else printf("\n Solutia aproximativa este: %f",x);} /*descrierea functiei pentru care se aplica metoda bisectiei*/ float fct(float x) { return x*x*x-3*x+14;} /*functia ce implementeaza metoda bisectiei*/ void bisectie(float a,float b,float (*f)(float),float eps,long n, int *cod,float *x) { int gata=0; long c; for(c=0;(c<n)&&!gata;c++) {*x=(a+b)/2; gata=fabs(*x-a)<eps; if ((*f)(*x)*(*f)(a)<0)b=*x; else a=*x;} *cod=gata;}

tiina nvrii unui limbaj de programare. Teorie

2. S se sorteze un ir cu elemente de un tip neprecizat, dar pe care se poate defini o relaie de ordine (de exemplu numeric, ir de caractere, caracter). Metoda aleas spre exemplificare este sortarea prin selecie direct. Un subprogram de sortare care s nu depind de tipul elementelor i de criteriul de sortare considerat trebuie s aib ca parametri formali: - vectorul de sortat, ca pointer la tipul void, asigurndu-se astfel posibilitatea realizrii operaiei de schimbare a tipului (cast) n funcie de necesitile ulterioare (la momentul apelului se poate realiza modificarea tipului *void n *tip_element, unde tip_element reprezint tipul elementelor vectorului de sortat); - dimensiunea vectorului de sortat i numrul de octei din reprezentarea tipului elementelor vectorului; - pointerul la o funcie de comparare, cu argumente de tip *void, care s permit la apel att schimbarea de tip, ct i descrierea efectiv a relaiei de ordine. Cum tipul elementelor vectorului nu este cunoscut la momentul descrierii procedurii de sortare, operaia de atribuire nu poate fi folosit, ea fiind nlocuit de o funcie de copiere a unui numr prestabilit de octei, de la o adres surs la una destinaie. O astfel de funcie exist n biblioteca mem.h, sintaxa ei fiind:
void *memmove(void *destinaie, const void *surs, unsigned n)

Pentru accesarea elementului de rang i din vector se folosete formula v+i*nr_octeti. Fiierul surs care conine funcia de sortare descris anterior este:
//fisier exp_tip.cpp #include <mem.h> include<malloc.h> int compara(const void *x, const void *y); void sort(void *v, int n, int dim, int (*compara)(const void *x,const void *y)) { int i,j; void *aux; aux=malloc(dim); for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if((*compara)((char*)v+dim*i,(char*)v+dim*j)) {memmove(aux,(char*)v+dim*i,dim); memmove((char*)v+dim*i,(char*)v+dim*j,dim); memmove((char*)v+dim*j,aux,dim);} free(aux);}

Exemplu de apel pentru un vector de numere reale:


#include <stdio.h> #include<conio.h> #include "exp_tip.cpp"

Subprograme
int compara(const void *a, const void *b) { if(*(float *)a>*(float *)b)return 1; else return 0;} void main() { float vect[20]; int n,i; clrscr(); printf("Dimensiunea vectorului:");scanf("%d",&n); printf("\nElementele:"); for(i=0;i<n;i++) scanf("%f",&vect[i]); sort(vect,n,sizeof(float),compara); printf("\nElementele sortate:"); for(i=0;i<n;i++) printf("\n%f",vect[i]); getch();}

Exemplu de apel pentru un vector de cuvinte:


#include <stdio.h> #include <string.h> #include<conio.h> #include "exp_tip.cpp" int compara(const void *a, const void *b) { if(strcmp((char *)a, (char *)b)>0)return 1; else return 0;} void main() { typedef char cuvant[10]; cuvant vect[20]; int n; clrscr(); printf("Dimensiunea vectorului de cuvinte:"); scanf("%d",&n); printf("\nCuvintele:"); for(int i=0;i<n;i++)scanf("%s",&vect[i]); sort(vect,n,10,compara); printf("\nCuvintele sortate:"); for(i=0;i<n;i++)printf("\n%s",vect[i]); getch();}

8.5 Funcii cu numr variabil de parametri


Bibliotecile limbajului C conin subprograme standard cu numr variabil de parametri (A). Spre deosebire de limbajul Pascal, limbajul C permite definirea funciilor utilizator cu numr variabil de parametri, prin utilizarea unui set de macrodefiniii, declarate n biblioteca stdarg.h, care permit accesul la lista de parametri. Fiierul stdarg.h declar tipul va_list i funciile va_start, va_arg i va_end, n care: - va_list este un pointer ctre lista de parametri. n funcia utilizator corespunztoare trebuie declarat o variabil (numit n continuare ptlist) de acest tip, care va permite adresarea parametrilor;

tiina nvrii unui limbaj de programare. Teorie

- va_start iniializeaz variabila ptlist cu adresa primului parametru din sublista variabil. Prototipul acestei funcii este:
void va_start(va_list ptlist, ultim)

unde ultim reprezint numele ultimului parametru din sublista variabil. n unele situaii (vezi exemplele), se transfer n acest parametru numrul de variabile trimise. - va_arg ntoarce valoarea parametrului urmtor din sub-lista variabil. Prototipul acestei funcii este:
tip_element va_arg(va_list ptlist, tip_element)

unde tip_element este tipul elementului transferat din list. Dup fiecare apel al funciei va_arg, variabila ptlist este modificat astfel nct s indice urmtorul parametru. - va_end ncheie operaia de extragere a valorilor parametrilor i trebuie apelat nainte de revenirea din funcie. Prototipul funciei este:
void va_end(va_list ptlist)

Problema numrului de parametri i tipurilor lor este tratat de programator. Exemple: 1. S se calculeze cel mai mare divizor comun al unui numr oarecare de numere ntregi.
#include<stdio.h> #include<conio.h> #include<stdarg.h> int cmmdc_var(int,...); int cmmdc(int, int); void main() { int x,y,z,w; clrscr(); scanf("%d%d%d%d",&x,&y,&z,&w); printf("\nCmmdc al primelor 3 numere:%d\n",cmmdc_var(3,x,y,z)); printf("\nCmmdc al tuturor numerelor:%d\n",cmmdc_var(4,x,y,z,w));} //cel mai mare divizor comun a doua numere int cmmdc(int x,int y) { int d=x,i=y,r; do{r=d%i; d=i;i=r;} while(r); return d;}

Subprograme
//cel mai mare divizor comun a nr numere int cmmdc_var(int nr,...) { va_list ptlist; /*initializarea lui ptlist cu adresa de inceput a listei de parametri*/ va_start(ptlist,nr); //extragerea primului parametru, de tip int x=va_arg(ptlist,int); for(int i=1;i<nr;i++){ //extragerea urmatorului element din lista de parametri y=va_arg(ptlist,int); z=cmmdc(x,y);x=z;} va_end(ptlist); return x;}

2. S se interclaseze un numr oarecare de vectori. Spre deosebire de exemplul anterior, n care n lista de parametri a funciei cu numr oarecare de parametri figurau elemente de acelai tip (int), acest exemplu ilustreaz modul de transfer i acces la elemente de tipuri diferite. Funciei intre_var i se transmit la apel vectorul rezultat, iar pentru fiecare vector de interclasat, adresa de nceput (pointer la tipul double) i numrul de elemente (int). Numrul parametrilor din lista variabil este, n acest, caz 2*numrul de vectori de interclasat.
#include<stdarg.h> #include<stdio.h> #include<conio.h> void inter(double *,int,double *,int,double *); void inter_var(double *,int nr,...); void main() { int n1,n2,n3,n4; double x1[10],x2[10],x3[10],x4[10],z[50]; clrscr(); scanf("%d%d%d%d",&n1,&n2,&n3,&n4); for(int i=0;i<n1;i++)scanf("%lf",&x1[i]); for(i=0;i<n2;i++)scanf("%lf",&x2[i]); for(i=0;i<n3;i++)scanf("%lf",&x3[i]); for(i=0;i<n4;i++)scanf("%lf",&x4[i]); inter_var(z,4,x1,n1,x2,n2); printf("\nRezultatul interclasarii primilor 2 vectori\n"); for(i=0;i<n1+n2;i++) printf("%lf ",z[i]); inter_var(z,8,x1,n1,x2,n2,x3,n3,x4,n4); printf("\nRezultatul interclasarii celor 4 vectori\n"); for(i=0;i<n1+n2+n3+n4;i++) printf("%lf ",z[i]);} void inter(double *x, int n1, double *y, int n2, double *z) { int i,j,k; for(i=0,j=0,k=0;(i<n1)&&(j<n2);k++) if(x[i]<y[j])z[k]=x[i++]; else z[k]=y[j++]; if(i<n1)for(;i<n1;z[k++]=x[i++]); else for(;j<n2;z[k++]=y[j++]);}

tiina nvrii unui limbaj de programare. Teorie


void inter_var(double *z,int nr,...) { va_list ptlist; double *x,*y,x1[100]; int n1,n2; /*initializarea lui ptlist cu adresa de parametri*/ va_start(ptlist,nr); //extragerea primului vector x=va_arg(ptlist,double *); //extragerea dimensiunii lui n1=va_arg(ptlist,int); for(int j=0;j<n1;j++)x1[j]=x[j]; for(int i=1;i<(int)(nr/2);i++) {//extragerea urmatorului vector y=va_arg(ptlist,double *); //extragerea numarului sau de elemente n2=va_arg(ptlist,int); inter(x1,n1,y,n2,z); for(j=0;j<n1+n2;j++)x1[j]=z[j];n1+=n2;} va_end(ptlist);}

inceput

listei

de

S-ar putea să vă placă și