Subprograme

PREZENTARE TEORETICA

1. Introducere

Subprogramele sunt părţi ale unui program, identificabile prin nume, care se pot activa la cerere prin intermediul acestor nume.Prezenţa subprogramelor implică funcţionarea în strânsă legătură a două noţiuni: definiţia unui subprogram şi apelul unui subprogram.Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul variabilelor virtuale (parametri formali) iar apelul unui subprogram nu este altceva decât execuţia procesului de calcul pentru cazuri concrete (cu ajutorul parametrilor reali, (efectivi, actuali) ).2. Structura unui subprogram C++Un subprogram (funcţie) are o definiţie şi atâtea apeluri câte sunt necesare.
Definiţia unei funcţii are forma generală:

tip_returnat nume_funcţie (lista parametrilor formali)
{
instrucţiune; // corpul funcţiei
}

Tip_returnat
Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi: int, char, char*, long, float, void, etc.

În cazul în care tipul rezultatului este diferit de void, corpul funcţiei trebuie să conţină cel puţin o instrucţiune return.
Înstrucţiunea return va specifica valoarea calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat.
Nume_funcţie
Reprezintă numele dat funcţiei de către cel ce o defineşte, pentru a o putea apela.
Lista_parametrilor_formali
Reprezintă o listă de declaraţii de variabile separate prin virgulă. Această listă poate să fie şi vidă.
Instrucţiune
Este o instrucţiune vidă, simplă sau compusă.



3. Apelul unei funcţii . Revenirea dintr-o funcţie

3.1 Apelul unei funcţii care nu returnează o valoare are forma generală:

nume_funcţie (lista parametrilor efectivi);
unde:
  • parametru efectiv = parametru actual = parametru real = parametru de apel
  • lista parametrilor efectivi = poate fi vidă, poate fi o expresie sau mai multe despărţite prin virgulă

Efectul instructiunii de apel este:
  • crearea tabelei de parametrii actuali;
  • crearea variabilelor locale functiei;
  • executarea corpului de instructiuni al functiei;
  • desfintarea tabelei de parametrii si a variabilelor locale;
  • revenirea la instruciunea urmatoare din program
O funcţie care returnează o valoare poate fi apelată fie printr-o instrucţiune de apel simplă (cazul funcţiilor care nu returnează valori) şi în plus poate fi apelată ca operand al unei expresii. În cazul în care funcţia se apelază print-o instrucţiune de apel simplă, rezultatul funcţiei se pierde.

Când funcţia se apelează ca operand, valoarea returnată va fi utilizată în expresie.
La apelul unei funcţii, valorile parametrilor efectivi se atribuie parametrilor formali corespunzători. În cazul în care unul din tipul unui paramatru efectiv diferă de tipul parametrului formal corespunzător, parametrul efectiv va fi convertit spre parametru formal (dacă este posibil, altfel compilatorul generează eroare).
În momentul în care se întâlneşte un apel de funcţie, controlul execuţiei programul este transferat primei instrucţiuni din funcţie, urmând a se executa secvenţial instrucţiunile funcţiei.
Revenirea dintr-o funcţie se face în unul din următoarele cazuri:
  • după execuţia ultimei instrucţiuni din corpul funcţiei
  • la întâlnirea unei instrucţiuni return
Instrucţiunea return
are formatele:

  • return ;
  • return expresie ;

Exemplul 3.1
Exemplul 3.2
  1. include
using namespace std;
void f1 ()
{
cout << "abc";
}
int main ()
{
f1();
}
#include
using namespace std;
void f1 (int k)
{
for (int i=1; i<=k ; i++)
cout << "abc"<< " ";
}
int main ()
{
f1(5);
}
Se va afisa:abc
Se va afişa:abc abc abc abc abc
Funcţia nu returnează o valoareFuncţia nu are parametriApelul funcţiei este o instrucţiune de apel simplă
Funcţia nu returnează o valoareFuncţia are un parametru formal de tip intApelul funcţiei este o instrucţiune de apel simplă şi se face cu ajutorul unui parametru actual care este de acelaşi tip cu tipul parametrului formal corespunzător


Exemplul 3.3
Exemplul 3.4
#include
using namespace std;
int prim (int x)
{int nr_div;
nr_div=0;
for (int i=2; i<=x/2; i++)
if (x%i==0)
nr_div++;
if (nr_div==0)
return 1;
else
return 0;
}
int main ()
{int N;
cout << "N="; cin >> N;
if (prim(N))
cout << "PRIM";
else
cout << "NU E PRIM";
}
include
using namespace std;
int prim (int x)
{int i;
for ( i=2; i<=x/2; i++)
if (x%i==0)
return 0;
return 1;
}
int main ()
{int N;
cout << "N="; cin >> N;
if (prim(N))
cout << "PRIM";
else
cout << "NU E PRIM";
}
Funcţia returnează o valoare de tip int Funcţia are un parametru formal de tip int Rezultatul funcţiei este este utilizat în cadrul unei expresii.
În cazul în care se întâlneşte un divizor a lui x se execută instrucţiunea return 0. Astfel apelul funcţiei se încheie.
Dacă x este număr prim, instrucţiunea return 0 nu se execută niciodată şi în acest caz, după terminarea execuţiei instrucţiunii for, se execută instrucţiunea return 1 (care determină încheierea execuţiei funcţiei).

OBS: În cazul în care tipul returnat de funcţie lipseşte din definiţia funcţiei, acesta este implicit
int şi nu void.

Exemplul 3.5
Exemplul 3.6
Exemplul 3.7
#include
using namespace std;
p( )
{
cout << " abcd";
}
int main ()
{
cout << p();
}
#include
using namespace std;
p( )
{
return 25;
}
int main ()
{
cout << p( );
}
#include
using namespace std;
void p( )
{
cout << "void";
}
int main ()
{
cout << p();
}
Compilatorul generează eroare deoarece lipseste tipul returnat de functie
Compilatorul generează eroare deoarece lipseste tipul returnat de functie
Compilatorul generează eroare deoarece o functie cu tipul returnatvoidnu se poate apela in cadrul unei functii, in cazul de fata cout

#include
using namespace std;
int p( )
{
return 25;
}
int main ()
{
cout << p( );
}
#include
using namespace std;
void p( )
{
cout << "abcd";
}
int main ()
{
p();
}

Se afişează 25
Se afişează abcd

4. Prototipul unei funcţii

Pentru a apela o funcţie, aceasta trebuie initial definită. Astfel apelul unei funcţii trebuie precedat de definiţia funcţiei respective.
O a doua posibilitate de apelare a funcţiei constă în scrierea prototipului funcţiei înainte ca acesta să fie apelată.
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei. Pot lipsi numele parametrilor formali (contează doar tipul şi ordinea acestora), în plus acesta este urmat de “;”.
Exemplul 4.1.

# include
using namespace std;

int max (int, int);

int main ()
{
cout << max(10, 20);
}

int max (int a, int b)
{
if (a>b)
return a;
else
return b;
}
PROTOTIPUL FUNŢIEI

APELUL FUNCŢIEI

DEFINIŢIA FUNCŢIEI
antetul funcţiei

corpul functiei

5. Variabile locale şi variabile globale
5.1 . Funcţia main
În C++ funcţia main determină prima instrucţiune pe care o va executa programul. Aceasta este unica diferenţă dintre main şi celelalte funcţii. Din acest motiv se poate spune că “orice se poate face în main se poate face şi în celelalte funcţii”.
5.2. Variabile locale
La fel cum se declară variabilele în cadrul funcţiei main, la fel se pot declara varibile si în cadrul celorlalte funcţii. Aceste variabile se numesc locale şi sunt accesibile doar in funcţia in care au fost declarate.
In cadrul unei funcţii se pot apela şi alte funcţii, cu conditia ca acestea sa fi fost definite înaintea eventualului apel sau este prezent un prototip de funcţie înaintea funcţiei apelate
5.3. Variabile globale
Variabilele globale
sunt declarate în afara oricărei funcţii şi sunt vizibile (pot fi utilizate) în tot programul (în programul principal şi în subprograme) din momentul declarării lor.

Exemplul 5.1
Exemplul 5.2
Exemplul 5.3
  1. include
using namespace std;
int N;
void f1()
{
int x=5;
N=10;
cout << endl<
cout << endl << x;
}
int main ()
{
N=4;
cout << N;
f1();
}
  1. include
using namespace std;
int N;
void f1()
{
int x=5;
cout << endl << x;
P=2;
}
int P=9;
int main ()
{f1();
cout << x;
P=7;
}
  1. include
using namespace std;
int N;
void f1(int p)
{
int x=p;
cout << x;
}
int main ()
{
f1(77);
}
N este variabilă globală si poate fi accesată în cadrul oricărei funcţii.

x este variabilă locală, vizibilă doar în cadrul funcţiei f1()

Se va afişa:
4
10
5
Compilatorul generează eroare deoarece
  • funcţia main încearcă să acceseze variabila locala x care este vizibilă doar în funcţia f1().
  • variabila P este accesată în f1() înainte de a fi declarată.
Se afişează 77

N este variabilă globală. Poate fi accesată în cadrul oricărei funcţii.
x este variabilă locală. Poate fi accesată doar în cadrul funcţiei f1()
p este parametru formal. Poate fi accesat doar în f1().

5.4. Regula de omonimie
În cazul în care există o variabilă locală care are acelaşi nume cu o variabilă globală, aceste două variabile se numesc variabile omonime.
Variabilele locale sunt prioritare (ascund) variabilele globale omonime.

Exemplul 5.4
  1. include
using namespace std;
int N=10;
void f1()
{
int N=2;
cout << N<<" ";
}
int main ()
{
f1();
cout << N;
}
Variabila N este definită atât ca variabilă globală cât şi ca variabilă locală în f1().
Se va afisa: 2 10
Funcţia f1() acţionează asupra variabilei locale N.
Funcţia main() acţionează supra variabilei globale N.


Întrebare. Cum gestionează compilatorul cele două variabile omonime ?
Răspuns:
Variabilelor globale li se rezervă spaţiu de memorie la începutul execuţiei programului, într-o zonă de memorie numită “zonă de date”. Acest spaţiu va fi ocupat până la încheierea execuţiei programului.
Variabilelor locale li se rezervă spaţiu într-o zonă specială de memorie numită “stiva”. La încheierea execuţiei subprogramului, conţinutul stivei este eliberat. Din acest motiv, variabilele locale sunt vizibile doar în interiorul subprogramului în care au fost declarate.
6. Parametrii formali şi parametrii actuali
Parametrii formali apar în antetul subprogramului şi sunt utilizaţi de subprogram pentru descrierea abstractă a unui proces de calcul .
Parametrii actuali apar în instrucţiunea de apelare a uni subprogram şi sunt folosiţi la execuţia unui proces de calcul pentru valori concrete.
Parametrii formali nu sunt variabile. O variabilă este caracterizată de nume, tip, şi adresă. Legarea unui parametru formal la o adresă se realizează în timpul execuţiei instrucţiunii de apelare a subprogramului.
7. Apel prin valoare şi apel prin referinţă
Există două tipuri de apel al subprogramelor:

  • Apel prin valoare
  • Apel prin referinţă
7.1. Apel prin valoare – se transmite o copie a parametrului actual.
Valorile transmise la apelul unui subprogram sunt memorate în stivă. Datorită faptului că, după terminarea execuţiei unui subprogram, stiva este eliberată, în cazul apelului prin valoare parametrul actual nu se modifică (se operează asupra unei copii a parametrului efectiv)
Parametrii transmisi prin valoare se pot modifica in corpul functiei dar dupa terminarea apelului functiei au aceasi valoare pe care au avut-o inainte de apel.
Se utilizează atunci când dorim ca subprogramul să lucreze cu acea valoare, dar să nu poată modifica parametrul efectiv corespunzător din blocul apelator.

Se pot transmite prin valoare:
a)Valorile reţinute de variabile. În acest caz parametrii efectivi trebuie să fie numele variabilelor.

Exemplu:

#include <iostream.h>
void test(int n) {
  n++;
  cout << n << endl;  // tipăreşte n=8
}
void main() {
  int n = 7;
  test(n);
  cout << n << endl;  // tipăreşte n=7
}

Parametrul n este transmis prin valoare. În funcţia main() acest parametru este iniţializat cu valoarea 7. Când apelăm funcţia test(), se rezervă spaţiu pe stivă, spaţiu care are numele parametrului formal (în acest caz, tot n) şi care este iniţializat cu valoarea memorată de variabila n a programului principal. Altfel spus, pe stivă se copie valoarea parametrului efectiv de apel. În funcţie, variabila n (care este locală acestei funcţii) este incrementată şi devine 8, valoare care va fi tipărită. La ieşirea din funcţie, variabila n din stivă se pierde, adică nu mai are spaţiu alocat, prin urmare valoarea 8 este pierdută. În main() se tipăreşte valoarea variabilei n (locală acesteia) care are valoarea 7.
Se observă că, în momentul apelului funcţiei test(), pe stivă sunt alocate două variabile cu acelaşi nume n. Prima variabilă este variabila locală funcţiei main() care se salvează pe stivă în momentul apelului pentru a putea reface contextul funcţiei main() după încheierea apelului. A doua variabilă este parametrul formal tip valoare n, vizibil numai în funcţia test() şi iniţializat în momentul apelului cu valoarea 7. Indiferent ce valori primeşte acest n în corpul funcţiei test(), după încheierea execuţiei acestei funcţii, spaţiul său este de alocat din stivă, adică variabila respectivă este distrusă. Din acest motiv, după execuţia funcţiei test(), conţinutul stivei este cel din dreapta. Se reface contextul din care s-a lansat apelul funcţiei test(), adică se recuperează din stivă valoarea variabilei locale n=7 şi adresa de revenire, adică adresa instrucţiunii cout.
b) Expresii. În acest caz, parametrii efectivi sunt expresii, care pot conţine şi funcţii şi care mai întâi se evaluează. Exemplu:

#include <iostream.h>
#include <math.h>

void test(int n) {
  cout << n << endl; 
}

void main() {
  test(5);                  // se va tipări 5
  test(7 + (int)sqrt(45));  // se va tipări 13
}

În funcţie se creează o variabilă numităn, reţinută pe stivă, care la primul apel va primi valoarea 5 şi la al doilea apel valoarea 13.
La ieşirea din funcţie conţinutul acestei variabile se pierde.
Transmiterea parametrilor prin valoarese utilizează când nu dorim ca subprogramul apelat să poată modifica parametrii efectivi de apel. Acesta este modul implicit de transmitere a parametrilor în limbajul C. Dacă nu ar exista decât transmiterea prin valoare, ar fi imposibil să modificăm valoarea anumitor valori care sunt declarate în blocul apelator.


7.2. Apel prin referinţă - se transmite adresa parametrului actual.
În cazul apelului prin referinţă, subprogramul, cunoscând adresa parametrului actual, acţionează direct asupra locaţiei de memorie indicată de aceasta, modificând valoarea parametrului actual.
Parametrii transmisi prin referinta se folosesc pentru transmiterea de rezultate in afara functiei. Ei se pot modifica in corpul functiei dar dupa terminarea apelului functiei au valoarea pe care au primit-o in timpul apelului functiei.
În C++, implicit, apelul se face prin valoare. Pentru a specifica un apel prin referinţă, în lista parametrilor formali, numele parametrului formal va trebui precedat de simbolul &

#include
using namespace std;
int a,b;
void afis(int a, int b)
{
a=a+1;
b=b+4;
cout< }

int main()
{
a=1;
b=2;
afis(a,b);
cout < }

#include
using namespace std;
int a,b;
void afis(int &a, int &b)
{
a=a+1;
b=b+4;
cout< }

int main()
{
a=1;
b=2;
afis(a,b);
cout < }

#include
using namespace std;
int a,b;
void afis(int &m, int n)
{
m=m+1;
n=n+4;
cout< }

int main()
{
a=1;
b=2;
cout < afis(a,b);
cout < }
Se va afisa:
2 6 (rezultatul apelului functiei afis)
1 2 (rezultatul de dupa apelul functiei afis)

Se va afisa:
2 6 (rezultatul apelului functiei afis)
2 6 (rezultatul de dupa apelul functiei afis)

Se va afisa:
1 2 (rezultatul inaintea apelului functiei afis)
2 6 (rezultatul apelului functiei afis)
2 2 (rezultatul de dupa apelul functiei afis: variabila a isi pastreaza
valoarea dupa apel fiind parametru transmis prin referinta pe cand
variabila b revine la valoarea pe care o avea inainte de apel fiind
parametru transmis prin valoare)


Exemplul 7.1
  1. include
using namespace std;
void schimba_valoare (int x, int y)
{
int z=x;
x = y;
y = z;
}
void schimba_referinta (int &a, int &b)
{
int aux=a;
a=b;
b=aux;
}
int main ()
{
int M=1, N=5;
schimba_valoare(M,N);
cout << "M="<schimba_referinta(M,N);
cout << "M="<}
APEL PRIN VALOARE

APEL PRIN REFERINŢĂ

Se va afişa:

M=1 N=5

M=5 N=1

FISA DE LUCRU 1.doc

FISE DE LUCRU 3_FISEdincapitolulsubprograme2009_2010.doc


TESTETEST_SUBPROGRAME.doc


APLICATII http://info.mcip.ro/?cap=Subprograme