Sunteți pe pagina 1din 12

3

TEHNICI DE PROGRAMARE N C

3.2 Structura programului. Functii


Un program C colectie de functii.
Functia main este obligatorie controleaza ntreaga executie a programului.
Concepte de construire a aplicatiilor stiintifice:
modularizarea descompunerea programului n unitati de program (functii sau
subrutine), care interactioneaza doar prin intermediul unor interfete clar definite
(liste de argumente).
ncapsularea controlul domeniului de vizibilitate al obiectelor n afara unitatilor
de program n care acestea sunt declarate.
Modularizare judicioas
a creste lizibilitatea programului, simplifica depanarea si
modificarea, face posibila reutilizarea functiilor de interes general n alte programe (construirea bibliotecilor de subrutine).
Modularizare excesiv
a programe disfunctionale si ineficiente.

Exemplu factorialul unui numar natural


Codificare cu variabile globale:
#include <stdio.h>
float f;
int n;

/* variabile globale */

/*=========================================================================*/
void Factorial(void)
{
int i;
f = 1.0;
for (i=2; i<=n; i++) f *= i;
}
/*=========================================================================*/
void main()
{
printf("n = "); scanf("%i",&n);
Factorial();
printf("factorial = %g\n",f);
}

Variabilele globale f si n pot fi modificate de orice functie a programului pot rezulta


efecte colaterale greu de controlat (nu este n spiritul programarii modulare).
Implementarea optim
a:
#include <stdio.h>
/*=========================================================================*/
float Fact(int n)
/*--------------------------------------------------------------------------Returneaza factorialul numarului intreg n (rezultat de tip float)
---------------------------------------------------------------------------*/
{
float f;
int i;
f = 1.0;
for (i=2; i<=n; i++) f *= i;
return f;
}
/*=========================================================================*/
void main()
{
int n;
printf("n = "); scanf("%i",&n);
printf("factorial = %g\n",Fact(n));
}

Functia Fact primeste valoarea n prin lista de argumente si returneaza factorialul prin
nume comunica strict prin antet.

Implementare folosind recursivitatea:


/*=========================================================================*/
float Fact(int n)
/*--------------------------------------------------------------------------Returneaza factorialul numarului intreg n utilizand recursivitatea
---------------------------------------------------------------------------*/
{
return ((n > 1) ? n * Fact(n-1) : 1.0);
}

Orice autoapel provoaca stocarea n heap (pe stiva) a unui nou set al variabilelor locale.
Trebuie asigurata iesirea din recursivitate n ordine inversa a nivelelor.
Eficienta scazuta.

3.4 Pointeri si liste de argumente


Pointer tip special de variabila, care are ca valori adrese.

&x
&x

FIGURA 3.1. Pointerii contin adresele variabilelor catre care pointeaza.

Pointerii pot fi utilizati pentru a face referire la variabile prin intermediul adreselor.
Operatorul adres
a & adresa unui obiect din memorie:
p = &x;

atribuie pointerului p adresa variabilei x p pointeaza catre x.


Operatorul de indirectare * acceseaza obiectul catre care pointeaza pointerul
*p reprezinta valoarea variabilei x.
Sintaxa declaratiei unui pointer: tip *nume .
tip indica tipul de variabile catre care poate sa pointeze pointerul respectiv (informatie
esentiala n procesul de indirectare).
int *p;

pointerul p pointeaza catre variabile de tip int.


Operatii cu pointeri:
Incrementarea unui pointer (p++) adresa urmatoarei locatii de memorie.
Decrementare (p--) adresa locatiei de memorie anterioare.
Incrementarea variabilei catre care pointeaza pointerul: ++(*p).
Utiliz
ari importante ale pointerilor n programarea stiintifica:
mecanismul returnarii variabilelor din functii prin lista de argumente
alocarea dinamica de memorie pentru tablouri.

Returnarea variabilelor din functii prin lista de argumente


n C argumentele functiilor sunt transmise prin valoare numai catre functia apelata.:
Functie apelanta date Functie apelata
Functia apelata nu poate modifica direct variabile din functia apelanta si deci sa returneze
valori.
Exemplu: functia Swap ar trebui sa interschimbe valorile celor doua argumente:
/*=========================================================================*/
void Swap(float x, float y)
/* nu returneaza nimic */
{
float temp;
temp = x;
x = y;
y = temp;
}
/*=========================================================================*/
void main()
{
float a, b;
...........
Swap(a,b);
...........
}

La intrare n Swap ax si by se interschimba nsa doar copii ale lui a si b.


Pentru ca Swap sa opereze direct asupra variabilelor a si b, trebuie sa primeasca adresele
lor, &a si &b:
/*=========================================================================*/
void Swap(float *x, float *y)
{
float temp;
temp = *x;
*x = *y;
*y = temp;
}
/*=========================================================================*/
void main()
{
float a, b;
...........
Swap(&a,&b);
...........
}

n Swap se opereaza asupra valorilor variabilelor a si b din programul principal prin


indirectarea pointerilor x si y.
5

3.5 Alocarea dinamica a tablourilor


Strnsa legatura ntre tablouri si pointeri orice operatie asupra elementelor unui tablou
poate fi realizata si cu ajutorul unui pointer.
int a[10];

defineste un tablou 10 locatii consecutive de memorie a[0], a[1],... ,a[9] (oset nul).
Tabloul a este alocat static, n faza de compilare a programului.
Prin definitie:
a
&a[0]

a[0]

a[1]

a[9]

&a[0]

FIGURA 3.2. Numele unui tablou este un pointer care contine adresa primului element al
tabloului.

Conform aritmeticii pointerilor:


a+1 echivalent cu &a[1] *(a+1) echivalent cu a[1]
a+i echivalent cu &a[i] *(a+i) echivalent cu a[i]
(compilatorul C face intern automat conversia).
Alocarea dinamic
a a tablourilor de oset arbitrar si lungime arbitrara:
W.H. Press, S.A. Teukolsky, W.T. Vetterling si B.P. Flannery, Numerical Recipes in C:
The Art of Scientific Computing (Cambridge University Press, Cambridge, 1992).
Functia malloc definita n stdlib.h
void *malloc((size_t) n)

returneaza un pointer catre un bloc contiguu neinitializat de n octeti din heap, sau NULL
daca cererea nu poate fi satisfacuta datorita indisponibilitatii n heap a unui bloc contiguu
de dimensiunea ceruta.
size_t tipul utilizat de limbajul C pentru dimensiunea obiectelor din memorie
(size_t) n conversia lui n la tipul size_t.
Pentru a returna un pointer catre un bloc de n componente de tipul float:
(float*) malloc((size_t) (n*sizeof(float)));

sizeof(tip ) returneaza lungimea n octeti a reprezentarii interne a tipului tip .


(float*) conversia pointerul ntr-un pointer catre locatii de tip real.
6

Alocarea dinamica n heap a unui bloc de n locatii reale:


float *Vector(int n)
{
float *p;
p = (float*) malloc((size_t) (n*sizeof(float)));
if (!p) {
printf("Vector: eroare de alocare !\n");
exit(1);
}
return p;
}

Context tipic de utilizare:


int i, n;
float *a;
n = . . . ;
a = Vector(n);

/* numele matricii este declarat ca pointer */


/* a devine pointer catre blocul alocat */

for (i=0; i<=n-1; i++) a[i] = . . . ;

/* a este folosit ca tablou */

Alocarea dinamica a unui tablou de oset imin, cu indicii ntre imin si imax:
numarul relevant de componente este imax-imin+1
prin atribuirea a=Vector(...), originea blocului alocat este asociata lui a[0], nu
lui a[imin] cum ar trebui trebuie sa se returneze o adresa anterioara cu imin
locatii fata de originea reala a blocului
/*=========================================================================*/
float *Vector(int imin, int imax)
/*--------------------------------------------------------------------------Aloca memorie pentru un vector cu componente de tip float, cu indicii in
intervalul [imin,imax]
---------------------------------------------------------------------------*/
{
float *p;
p = (float*) malloc((size_t) ((imax-imin+1)*sizeof(float)));
if (!p) {
printf("Vector: eroare de alocare !\n");
exit(1);
}
return p - imin;
}

Functie pereche pentru a elibera blocul din heap alocat de functia Vector.
/*=========================================================================*/
void FreeVector(float *p, int imin)
/*--------------------------------------------------------------------------Dealoca memoria alocata de functia Vector pentru un vector cu componente de
tip float cu offsetul imin
---------------------------------------------------------------------------*/
{
free((void*) (p+imin));
}

Context tipic de utilizare (nu trebuie folosite componentele a[0],..., a[imin-1]):


int i, imin, imax;
float *a;
imin = . . . ;
imax = . . . ;
a = Vector(imin,imax);
for (i=imin; i<=imax; i++) a[i] = . . . ;
FreeVector(a,imin);

Tablouri bidimensionale
Numele unui tablou bidimensional este un pointer catre un tablou de pointeri de linie,
fiecare dintre acestia indicnd primul element din linia corespunzatoare.
a
&a[0]

a[0]
&a[0][0]

a[0][0]

a[0][1]

a[0][2]

a[0][3]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[2][0]

a[2][1]

a[2][2]

a[2][3]

a[1]
&a[1][0]

a[2]
&a[2][0]

FIGURA 3.3. Schema de stocare n memorie a unei matrici 3 4. Numele tabloului este un
pointer catre un tablou de pointeri de linie.

Declaratia tabloului: float **a.


Referirea la tablou n ansamblu a.
Referirea la o ntreaga linie a[i].
Referirea la un element a[i][j].

Functia Matrix aloca un tablou bidimensional cu elemente reale:


/*=========================================================================*/
float **Matrix(int imin, int imax, int jmin, int jmax)
/*--------------------------------------------------------------------------Aloca memorie pentru o matrice cu componente de tip float, cu indicii de
linie in intervalul [imin,imax] si indicii de coloana in intervalul
[jmin,jmax]
---------------------------------------------------------------------------*/
{
int i, ni = imax-imin+1, nj = jmax-jmin+1;
float **p;
/* aloca pointerul catre pointerii de linie */
p = (float**) malloc((size_t)(ni*sizeof(float*)));
if (!p) {
printf("Matrix: eroare de alocare de nivel 1 !\n");
exit(1);
}
p -= imin;
/* aloca pointerul pentru prima linie */
p[imin] = (float*) malloc((size_t)(ni*nj*sizeof(float)));
if (!p[imin]) {
printf("Matrix: eroare de alocare de nivel 2 !\n");
exit(2);
}
p[imin] -= jmin;
/* aloca pointerii pentru restul liniilor */
for (i = imin+1; i <= imax; i++) p[i] = p[i-1] + nj;
return p;
}

Functia pereche care elibereaza blocul de memorie alocat:


/*=========================================================================*/
void FreeMatrix(float **p, int imin, int jmin)
/*--------------------------------------------------------------------------Dealoca memoria alocata de functia Matrix pentru o matrice avand componente
de tip float, cu offsetul de linie imin si offsetul de coloana jmin
---------------------------------------------------------------------------*/
{
free((void*) (p[imin]+jmin));
free((void*) (p+imin));
}

Context tipic de utilizare:


int i, imin, imax, j, jmin, jmax;
float **a;
imin = . . . ; imax = . . . ;
jmin = . . . ; jmax = . . . ;
a = Matrix(imin,imax,jmin,jmax);
for (i=imin; i<=imax; i++)
for (j=jmin; j<=jmax; j++)

a[i][j] = . . . ;

FreeMatrix(a,imin,jmin);

Fisierul header memalloc.h contine functii de alocare si dealocare pentru tablouri uni- si
bidimensionale pentru tipuri uzuale.
9

3.6 Operatii simple cu matrici


(A B)T BT AT = 0,
#include <stdio.h>
#include "memalloc.h"
/*=========================================================================*/
void MatRead(float **a, int n, int m)
/*--------------------------------------------------------------------------Citeste de la tastatura componentele matricii a[][], cu n linii si m coloane
---------------------------------------------------------------------------*/
{
int i, j;
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++) {
printf("[%i][%i]=",i,j); scanf("%f",&a[i][j]);
}
}
/*=========================================================================*/
void MatPrint(float **a, int n, int m)
/*--------------------------------------------------------------------------Tipareste pe ecran componentele matricii a[][], cu n linii si m coloane
---------------------------------------------------------------------------*/
{
int i, j;
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) printf("%10.2e",a[i][j]);
printf("\n");
}
}
/*=========================================================================*/
void MatTrans(float **a, int n)
/*--------------------------------------------------------------------------Inlocuieste matricea patrata a[][] de ordinul n cu transpusa ei
---------------------------------------------------------------------------*/
{
float t;
int i, j;
for (i = 1; i <= n-1; i++)
for (j = i+1; j <= n; j++) {
t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t;
}
}
/*=========================================================================*/
void MatDiff(float **a, float **b, float **c, int n, int m)
/*--------------------------------------------------------------------------Calculeaza diferenta matricilor a[][] si b[][] cu n linii si m coloane si o
returneaza in matricea c[][]
---------------------------------------------------------------------------*/
{
int i, j;
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++) c[i][j] = a[i][j] - b[i][j];
}
/*=========================================================================*/
void MatProd(float **a, float **b, float **c, int l, int m, int n)

10

/*--------------------------------------------------------------------------Calculeaza produsul dintre matricea a[][] cu l linii si m coloane si matricea


b[][] cu m linii si n coloane, si returneaza rezultatul in matricea c[][]
---------------------------------------------------------------------------*/
{
float t;
int i, j, k;
for (i = 1; i <= l; i++)
for (j = 1; j <= n; j++) {
t = 0.0;
for (k = 1; k <= m; k++) t += a[i][k] * b[k][j];
c[i][j] = t;
}
}
/*=========================================================================*/
void main()
/*--------------------------------------------------------------------------Verifica identitatea matriciala (A B)_trans = B_trans A_trans
---------------------------------------------------------------------------*/
{
float **a, **b, **c, **d;
int n;
printf("n = "); scanf("%i",&n);
a
b
c
d

=
=
=
=

Matrix(1,n,1,n);
Matrix(1,n,1,n);
Matrix(1,n,1,n);
Matrix(1,n,1,n);

/* aloca matricile */

printf("matricea a\n");
MatRead(a,n,n);
printf("matricea b\n");
MatRead(b,n,n);

/* citeste matricile */

MatProd(a,b,c,n,n,n);
MatTrans(c,n);

/* (A B)_trans */

MatTrans(a,n);
MatTrans(b,n);
MatProd(b,a,d,n,n,n);

/* B_trans A_trans */

MatDiff(c,d,d,n,n);
MatPrint(d,n,n);

/* (A B)_trans - B_trans A_trans */

FreeMatrix(a,1,1);
FreeMatrix(b,1,1);
FreeMatrix(c,1,1);
FreeMatrix(d,1,1);

/* dealoca matricile */

11

Bibliography

[1] W.H. Press, S.A. Teukolsky, W.T. Vetterling si B.P. Flannery, Numerical Recipes in
C: The Art of Scientific Computing, Second Edition (Cambridge University Press,
Cambridge, 1992).
[2] B.W. Kernighan si D.M. Ritchie, The C Programming Language, Second Edition
(Prentice-Hall, Englewood Clis, NJ, 1988).
[3] E. Yourdon, Techniques of Program Structure and Design (Prentice-Hall, Englewood
Clis, NJ, 1975).

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