Documente Academic
Documente Profesional
Documente Cultură
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().
unde: tip poate fi un tip simplu de dat. Dac lipsete, este considerat tipul implicit (int pentru unele compilatoare, void pentru altele);
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!");}
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);
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;}
b)
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
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;}
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();}
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.
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;}
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);}
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();}
- 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++]);}
inceput
listei
de