Sunteți pe pagina 1din 12

3

TEHNICI DE PROGRAMARE ÎN C

3.2 Structura programului. Funcţii


Un program C – colecţie de funcţii.

Funcţia main este obligatorie – controlează întreaga execuţie a programului.

Concepte de construire a aplicaţiilor ştiinţifice:

• modularizarea – descompunerea programului în unităţi de program (funcţii sau


subrutine), care interacţionează doar prin intermediul unor interfeţe clar definite
(liste de argumente).

• încapsularea – controlul domeniului de ”vizibilitate” al obiectelor în afara unităţilor


de program în care acestea sunt declarate.

Modularizare judicioasă – creşte lizibilitatea programului, simplifică depanarea şi


modificarea, face posibilă reutilizarea funcţiilor de interes general în alte programe (con-
struirea bibliotecilor de subrutine).

Modularizare excesivă – programe disfuncţionale şi ineficiente.


Exemplu – factorialul unui număr natural
Codificare cu variabile globale:

#include <stdio.h>
float f; /* variabile globale */
int n;
/*=========================================================================*/
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 şi n pot fi modificate de orice funcţie a programului – pot rezulta
efecte colaterale greu de controlat (nu este în spiritul programării modulare).

Implementarea optimă:

#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));
}

Funcţia Fact primeşte valoarea n prin lista de argumente şi returnează factorialul prin
nume – comunică strict prin antet.

2
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 provoacă stocarea în heap (pe stivă) a unui nou set al variabilelor locale.
Trebuie asigurată ieşirea din recursivitate în ordine inversă a nivelelor.
Eficienţă scăzută.

3
3.4 Pointeri şi liste de argumente
Pointer – tip special de variabilă, care are ca valori adrese.

p x
&x
&x

FIGURA 3.1. Pointerii conţin adresele variabilelor către care pointează.

Pointerii pot fi utilizaţi pentru a face referire la variabile prin intermediul adreselor.

Operatorul adresă & – adresa unui obiect din memorie:

p = &x;

atribuie pointerului p adresa variabilei x – p pointează către x.

Operatorul de indirectare * – accesează obiectul către care pointează pointerul –


*p reprezintă valoarea variabilei x.

Sintaxa declaraţiei unui pointer: tip *nume .


tip indică tipul de variabile către care poate să pointeze pointerul respectiv (informaţie
esenţială în procesul de indirectare).

int *p;

pointerul p pointează către variabile de tip int.

Operaţii cu pointeri:
Incrementarea unui pointer (p++) → adresa următoarei locaţii de memorie.
Decrementare (p--) → adresa locaţiei de memorie anterioare.
Incrementarea variabilei către care pointează pointerul: ++(*p).

Utilizări importante ale pointerilor în programarea ştiinţifică:

• mecanismul returnării variabilelor din funcţii prin lista de argumente

• alocarea dinamică de memorie pentru tablouri.

4
Returnarea variabilelor din funcţii prin lista de argumente
În C argumentele funcţiilor sunt transmise prin valoare – numai către funcţia apelată.:

Funcţie apelantă → date → Funcţie apelată

Funcţia apelată nu poate modifica direct variabile din funcţia apelantă şi deci să returneze
valori.

Exemplu: funcţia Swap ar trebui să interschimbe valorile celor două 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 a→x şi b→y – se interschimbă însă doar copii ale lui a şi b.

Pentru ca Swap să opereze direct asupra variabilelor a şi b, trebuie să primească adresele
lor, &a şi &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 operează asupra valorilor variabilelor a şi b din programul principal prin
indirectarea pointerilor x şi y.

5
3.5 Alocarea dinamică a tablourilor
Strânsă legătură între tablouri şi pointeri – orice operaţie asupra elementelor unui tablou
poate fi realizată şi cu ajutorul unui pointer.

int a[10];

defineşte un tablou – 10 locaţii consecutive de memorie a[0], a[1],... ,a[9] (offset nul).

Tabloul a este alocat static, în faza de compilare a programului.

Prin definiţie:

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

FIGURA 3.2. Numele unui tablou este un pointer care conţine 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 tablourilor de offset arbitrar şi lungime arbitrară:

W.H. Press, S.A. Teukolsky, W.T. Vetterling şi B.P. Flannery, Numerical Recipes in C:
The Art of Scientific Computing (Cambridge University Press, Cambridge, 1992).

Funcţia malloc definită în stdlib.h

void *malloc((size_t) n)

returnează un pointer către un bloc contiguu neiniţializat de n octeţi din heap, sau NULL
dacă cererea nu poate fi satisfăcută datorită indisponibilităţii în heap a unui bloc contiguu
de dimensiunea cerută.
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 către un bloc de n componente de tipul float:

(float*) malloc((size_t) (n*sizeof(float)));

sizeof(tip ) returnează lungimea în octeţi a reprezentării interne a tipului tip .


(float*) – conversia pointerul într-un pointer către locaţii de tip real.

6
Alocarea dinamică în heap a unui bloc de n locaţii 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; /* numele matricii este declarat ca pointer */
n = . . . ;
a = Vector(n); /* a devine pointer catre blocul alocat */
for (i=0; i<=n-1; i++) a[i] = . . . ; /* a este folosit ca tablou */

Alocarea dinamică a unui tablou de offset imin, cu indicii între imin şi imax:
• numărul relevant de componente este imax-imin+1
• prin atribuirea a=Vector(...), originea blocului alocat este asociată lui a[0], nu
lui a[imin] cum ar trebui → trebuie să se returneze o adresă anterioară cu imin
locaţii faţă de originea reală 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;
}

Funcţie pereche pentru a elibera blocul din heap alocat de funcţia 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));
}

7
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 către un tablou de pointeri de linie,
fiecare dintre aceştia indicând primul element din linia corespunzătoare.

a a[0]
&a[0] &a[0][0] a[0][0] a[0][1] a[0][2] a[0][3]

a[1]
&a[1][0] a[1][0] a[1][1] a[1][2] a[1][3]

a[2]
&a[2][0] a[2][0] a[2][1] a[2][2] a[2][3]

FIGURA 3.3. Schema de stocare în memorie a unei matrici 3 × 4. Numele tabloului este un
pointer către un tablou de pointeri de linie.

Declaraţia tabloului: float **a.


Referirea la tablou în ansamblu → a.
Referirea la o întreagă linie → a[i].
Referirea la un element → a[i][j].

8
Funcţia Matrix – alocă 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;
}

Funcţia pereche care eliberează 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);

Fişierul header memalloc.h conţine funcţii de alocare şi dealocare pentru tablouri uni- şi
bidimensionale pentru tipuri uzuale.

9
3.6 Operaţii 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 = Matrix(1,n,1,n); /* aloca matricile */
b = Matrix(1,n,1,n);
c = Matrix(1,n,1,n);
d = Matrix(1,n,1,n);
printf("matricea a\n"); /* citeste matricile */
MatRead(a,n,n);
printf("matricea b\n");
MatRead(b,n,n);
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); /* (A B)_trans - B_trans A_trans */
MatPrint(d,n,n);
FreeMatrix(a,1,1); /* dealoca matricile */
FreeMatrix(b,1,1);
FreeMatrix(c,1,1);
FreeMatrix(d,1,1);
}

11
Bibliography

[1] W.H. Press, S.A. Teukolsky, W.T. Vetterling şi B.P. Flannery, Numerical Recipes in
C: The Art of Scientific Computing, Second Edition (Cambridge University Press,
Cambridge, 1992).

[2] B.W. Kernighan şi D.M. Ritchie, The C Programming Language, Second Edition
(Prentice-Hall, Englewood Cliffs, NJ, 1988).

[3] E. Yourdon, Techniques of Program Structure and Design (Prentice-Hall, Englewood


Cliffs, NJ, 1975).

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