BluePink BluePink
XHost
Gazduire site-uri web nelimitata ca spatiu si trafic lunar la doar 15 eur / an. Inregistrare domenii .ro .com .net .org .info .biz .com.ro .org.ro la preturi preferentiale. Pentru oferta detaliata accesati site-ul BluePink

SUBPROGRAME IN C/C++

Prezenta functiilor subprogram in cadrul unei aplicatii C/C++ se datoreste unei necesitati logice de a divide rezolvarea unei probleme complicate in subprobleme mai simple.

De asemenea nu este necesar sa reinventam roata de cate ori scriem un program. Anumite parti ale sale pot fi comune cu ale altor programe si de aceea este mai bine sa le pastram definite separat si sa le adaugam la nevoie. Aceasta operatie se numeste reutilizarea codului sursa.

Exista si posibilitatea sa grupam anumite subprograme in biblioteci gata compilate si sa le adaugam in faza de editare de legaturi. Asa se intampla cu subprogramele de citire/scriere din "iostream.h" sau "math.h".

Un subprogram se defineste incepand cu antetul sau si continuand cu corpul. Astfel:

O functie care intoarce o singura valoare prin mecanismul return se numeste functie operand. Apelul ei are forma: nume_variabila = nume_fc(lista de argumente);

Definirea unei functii trebuie sa fie plasata inaintea programului apelant in fisier sau, daca este adaugata mai jos trebuie sa existe o declaratie a antetului functiei in aceasta pozitie: Exemplu:

.....................
int suma(int x, int y)
{  int s=0;
   s = x+y;
   return s;
}

int main()
{  int a,b,c;
    cout << "a: "; cin >> a;
    cout << "b: "; cin >> b;
    c = suma(a,b); //apelul functiei subprogram
..................................

....................
int suma(int x, int y);

int main()
{  int a,b,c;
    cout << "a: "; cin >> a;
    cout << "b: "; cin >> b;
    c = suma(a,b);
..................................}

int suma(int x, int y);
{  int s=0;
   s = x+y;
   return s;
}

Daca datele de intrare intr-o functie devin si date de iesire sau numai o parte dintre acestea discutam despre o functie procedura. Adesea aceasta nu intoarce inca o valoare prin mecanismul return. In acest caz apelul are forma: nume_fc(lista de argumente);

Unui program C/C++ i se aloca urmatoarele segmente de memorie: segmentul de cod, segmentul de date si segmentul de stiva. Optional se adauga si memoria HEAP pentru date alocate dinamic = pointeri.

In segmentul de cod se memoreaza programul principal main() si corpurile subprogramelor. In segmentul de date se memoreaza toate datele alocate static, adica datele declarate in afara lui main(). Datele declarate in cadrul unui subprogram se numesc date locale si se memoreaza in segmentul de stiva in timpul executiei subprogramului. La terminarea subprogramului ele dispar din stiva.

In momentul in care din firul principal de executie trecem la un subprogram se memoreaza urmatoarele date pe stiva (stack):

O stiva se umple "in mod grafic" de jos in sus si se goleste invers de sus in jos - adica ultima data memorata se scoate prima din stiva. Adresa cea mai de jos este cea mai mare si stiva se umple catre adrese din ce in ce mai mici. Cand trecem de la main() la primul subprogram apelat deja stiva va contine adresa de inceput a lui main() si datele sale locale. 

Daca una sau mai multe dintre acestea se transmit functiei subprogram prin mecanismul adresa sau referinta adresa care se copiaza in zona datelor de intrare in subprogram va fi luata tot din stiva, din zona variabile locale ale lui main(). Si atunci se lucreaza de fapt direct pe variabilele programului apelant care se vor modifica. 

Altfel, daca este vorba de transmitere prin valoare se copiaza atat datele statice - din segmentul de date - cat si cele din alte zone ale stivei in zona de stiva alocata listei de argumente si care se sterge odata cu terminarea functiei subprogram. Orice modificare a lor se face la noile adrese si nu se reflecta in programul apelant. De aceea rezultatele obtinute trebuie transmise prin return. Daca sunt mai multe trebuie grupate intr-un tip de data "struct". Tipul de date tablou nu se poate returna astfel deoarece numele tabloului este de fapt un pointer la primul sau element. Adica el stie numai adresa primului element, care se pierde oricum la terminarea subprogramului.

Tablourile se pot insa utiliza in lista de argumente si mecanismul de transmitere este prin adresa, se vor modifica toate elementele din vectorul declarat in programul apelant  si atunci nu mai este nevoie de mecanismul return.

Exemple:

 Suma a 2 numere a si b, intregi, transmise prin valoare:

.............adresa variabilei locale s din suma(x,y)

.............adresele pentru x si y

.............adresa de inceput suma(x,y)

..............adresa de revenire dupa apel functie suma(x,y)

..............adresele variabilelor locale a si b din main()

..............adresa de inceput main()

Incarcarea cu valorile 0..9 a unui vector de intregi dat prin referinta are urmatorul program:

#include <iostream.h>
#include <conio.h>

void vector(int v[10])
{
  for(int i=0;i<10;i++)
    v[i] = i;
}

int main()
{
    int x[10];
   
    vector(x);
    cout << endl;
    for(int i=0;i<10;i++)
        cout << x[i] << " ";
    return 0;
}

............adresa i (cat tine blocul for!)

............adresa v = adresa x

............adresa de inceput vector()

............adresa de revenire din apel vector()

............adresa lui x[] 

.............adresa de inceput a lui main()

Nu ne vom lasa pacaliti de aparenta sintaxa prin valoare. Vectorul v este deja o adresa, nu are nevoie de notatia cu * ca pointer sau cu & ca referinta. Si nu vom uita ca o referinta este de fapt un al doilea nume pentru aceeasi adresa de memorie. Daca schimbam forma de adresare in functia suma(x,y) vom avea suma(&x,&y){....} si atunci a si x vor avea aceeasi adresa, la fel b si y.