Capitolul 4

Functii si programare structurata

Programarea structurata este o problema ce rezolva strategia si metodologia programarii si are urmatoarele principii:

1. Structurile de control trebuie sa fie cat se poate de simple;
2. Constructia unui program trebuie sa fie descrisa top-down
.

Descrierea top-down se refera la descompunerea problemei noastre in subprobleme. De obicei, aceste subprobleme sunt usor de descris.

4.1 Apelul functiilor

Un program este compus din una sau mai multe functii, printre care si "main()". Intotdeauna executia unui program incepe cu "main()". Cand o functie este apelata (sau invocata) atunci controlul programului este pasat functiei apelate. Dupa ce aceasta isi termina executia, atunci se paseaza inapoi controlul catre program.
Codul C care descrie ce face o functie se numeste "definitia functiei". Aceasta are urmatoarea forma generala:
tip nume_functie (lista_parametri)
{
declaratii
instructiuni
}

Primul rand se numeste "header-ul" (antetul) functiei, iar ceea ce este inclus intre acolade se numeste corpul functiei. Daca in antet nu precizam parametri, atunci se va scrie "void" (cuvant rezervat pentru lista vida). Daca functia nu intoarce nici o valoare, atunci se va scrie ca tip intors tot "void". Tipul intors de functie este cel precizat in "return" (ce va fi indata explicat). Parametrii din antetul functiei sunt dati printr-o lista cu argumente separate prin virgula. Aceste argumente sunt date de tipul argumentului urmat de un identificator ce apartine acelui tip. Se mai spune ca acel identificator este "parametru formal".

Exemplu:

#include
void tipareste_mesaj(int k)
{
int i;
printf("Iti urez:\n");
for (i = 0; i < k; ++i)
printf(" O zi buna ! \n");
}

main()
{
int n;
printf("Dati un numar natural mic: ");
scanf("%d", &n);
tipareste_mesaj(n);
}

4.2 Instructiunea "return"

Instructiunea "return" este folosita pentru doua scopuri. Cand se executa o instructiune "return", controlul programului este pasat inapoi programului apelant. In plus, daca exista o expresie dupa acest "return", atunci se va returna valoarea acestei expresii. Instructiunea "return" poate avea formele:
return;
sau
return expresie;

Exemplu: Minimul a doi intregi.

#include
int min(int x, int y)
{
if (x < y)
return x;
else
return y
}

main()
{
int j, k, m;

printf("Dati doi intregi: ");
scanf("%d%d", &j, &k);
m = min(j, k);
printf("\n%d este minimul dintre %d si %d.\n", m, j, k);
}

4.3 Prototipurile functiilor

In C, apelul unei functii poate apare inaintea declararii ei. Functia poate fi definita mai tarziu in acelasi fisier, sau in alt fisier sau dintr-o biblioteca standard. In ANSI C, prototipul functiei remediaza problema punand la dispozitie numarul si tipul argumentelor functiei. Prototipul specifica, de asemenea, si tipul returnat de functie. Sintaxa prototipului unei functii este:
tip nume_functie (lista_tipuri_parametri);
In lista de parametri putem specifica chiar si parametrul, dar asta este optional. Daca functia nu are parametri, atunci se foloseste "void".

Exemplu: Reluam un exemplu precedent.

#include
main()
{
int n;
void tipareste_mesaj(int);

printf("Dati un numar natural mic: ");
scanf("%d", &n);
tipareste_mesaj(n);
}

void tipareste_mesaj(k)
{
int i;

printf("Iti urez:\n");
for (i = 0; i < k; ++i)
printf(" O zi buna ! \n");
}

Prototipul unei functii poate fi plasat in corpul altei functii, sau de regula, se scriu la inceputul programelor dupa directivele #include si #define.

4.4 Descriere "top-down"

Presupunem ca avem de citit cativa intregi si trebuie sa-i afisam in ordine pe coloane (in capatul de sus al coloanelor trebuie sa scriem numele campului), sa le afisam suma lor partiala, minimul si maximul lor. Pentru scrierea unui program C ce face acest lucru, vom utiliza proiectarea (descrierea) "top-down".

Astfel, descompunem problema in urmatoarele subprobleme:

1. Un antet pentru problema data;
2. Scrierea campurilor;
3. Citirea si scrierea lor pe coloane.

Toti acesti trei pasi vor fi descrisi in cate o functie ce se apeleaza din "main()". Obtinem, un prim cod:

#include

main()
{
void tipareste_antet(void);
void scrie_campurile(void);
void citeste_scrie_coloanele(void);

tipareste_antet();
scrie_campurile();
citeste_scrie_coloanele();
}


Aceasta reprezinta intr-un mod foarte simplu descrierea "top-down". Daca o problema este prea grea, atunci o descompunem in subprobleme, si apoi le rezolvam pe acestea. Beneficiul suplimentar al acestei metode este claritatea sa.
void tipareste_antet(void)

{
printf("\n%s%s%s\n",
"**************************************************\n",
"* Calculul sumelor, minimului si maximului *\n",
"**************************************************\n");
}


Functia ce foloseste la scrierea campurilor este la fel usor de scris:
void scrie_campurile(void)
{
printf("%5s%12s%12s%12s%12s\n\n",
"Numar", "Articol", "Suma", "Minimul", "Maximul");
}

Urmeaza apoi functia ce serveste la scrierea inregistrarilor referitoare la campurile discutate mai sus:
void citeste_scrie_coloanele(void)
{
int contor = 0, articol, suma, minim, maxim;
int min(int, int), max(int, int);

if (scanf("%d", &articol) == 1)
{
++contor;
suma = minim = maxim = articol;
printf("%5d%12d%12d%12d%12d\n\n",
contor, articol, suma, minim, maxim);
while (scanf("%d", &articol) == 1)
{
++contor;
suma += articol;
minim = min(articol, minim);
maxim = max(articol, maxim);
printf("%5d%12d%12d%12d%12d\n\n",
contor, articol, suma, minim, maxim);
}
}
else
printf("Nici o data nu a fost citita.\n\n");
}

Daca datele se introduc de la tastatura, atunci tabelul se va afisa "intrerupt" de citirile ce au loc de la tastatura. Astfel, se prefera citirea dintr-un fisier extern. Presupunem ca fisierul nostru executabil (asociat fisierului sursa scris in C) se numeste "numere.exe" si am creat un fisier numit "fisier.int" ce contine urmatoarele numere:
19 23 -7 29 -11 17
Dand comanda numere < fisier.int vom obtine un tabel ce contine toate datele dorite.

4.5 Invocare si apel prin valoare

O functie este invocata prin scrierea numelui sau impreuna cu lista sa de argumente intre paranteze. De obicei, numarul si tipul acestor argumente se "potriveste" cu parametrii din lista de parametri prezenti in definitia functiei. Toate argumentele sunt apelate prin valoare ("call-by-value"). Asta inseamna ca fiecare argument este evaluat si valoarea sa este folosita ca valoare pentru parametrul formal corespunzator. De aceea, daca o variabila (argument) este folosita la transmiterea unei valori, atunci valoarea ei nu se schimba.

Exemplu:

#include
main()
{
int n=3, suma, calculeaza_suma(int);

printf("%d\n", n); /* se va scrie 3 */
suma = calculeaza_suma(n);
printf("%d\n", n); /* se va scrie 3 */
printf("%d\n", suma); /* se va scrie 6 */
}

int calculeaza_suma(int n) /* suma numerelor de la 1 la n */
{
int suma = 0;
for ( ; n > 0; --n) /* n se schimba aici, dar nu si in main() */
sum += n;
printf("%d\n", n); /* se va scrie 0 */
return suma;
}

Chiar daca n este trimis ca argument in functia "calculeaza_suma()" si valoarea lui n se modifica in aceasta functie, valoarea sa din mediul apelant ramane neschimbata. Vom vedea mai tarziu cum se poate simula apelul prin adresa ("call-by-reference").

4.6 Deosebirea dintre "return" si "exit"

Exista doua procedee de a returna o valoare.
return expresie si exit(expresie)

Daca se folosesc in "main()", atunci acestea sunt echivalente, dar in orice alta functie efectul lor este diferit (in ANSI C, functia "main()" intoarce o valoare de tip int). Un apel al lui exit() in orice alta functie va implica terminarea executiei programului si returnarea valorii catre mediul apelant (sistemul de operare sau mediul de programare C). Valoarea returnata se numeste stare de iesire ("exit status"). Prin conventie, starea de iesire zero indica terminare cu succes, pe cand iesire cu un numar diferit de zero indica o situatie anormala.