Page 1 of 1

[C++] Care sunt metodele de transmitere a parametrilor?

Posted: 31 Jul 2007, 10:21
by Marius Bancila
Intrebare: Care sunt modalitatile de transmitere a parametrilor la functii?

Raspuns: In C++ sunt doua modalitati: prin valoare (copiere) sau prin referinta. Prin valoarea inseamna ca o copie a obiectului original se face pe stiva apelatului, iar modifcarea acestuia in functia apelata nu afecteaza obiectul din apelant. Pe de alta parte, in cazul transmiterii prin referinta (care contine adresa unui obiect, dar se comporta ca un obiect) modificarile in apelat inseamna modificari directe asupra obiectului din apelant. Se poate deosebi intre cele doua modalitati astfel: daca parametrul e precedat de & atunci e transmitere prin referinta; altfel e prin valoarea (copiere).


Intrebare: Dar ce se intampla in cazul transmiterii prin pointer?

Raspuns: Nu exista asa ceva. E o intelegere gresita. Un pointer este o variabila care contine o adresa de memorie. Se poate transmite un pointer, dar acesta este transmis prin copiere, adica o copie a sa se face pe stiva apelantului.

Voi explica mai jos cu cateva exemple de cod.

In primul exemplu un intreg este pasat unei functii foo() care il incrementeaza si il afiseaza. Pentru ca o copie este facuta pe stiava lui foo(), programul va afisa 0 1 0:

Code: Select all


void foo(int i)
{
  i++;
  cout << i << endl;
}

int main()
{
  int i = 0;

  cout << i << endl;
  foo(i);
  cout << i << endl;
  
  return 0;
}
Intrucat transmiterea prin valoarea necesita o copiere, in cazul tipurilor definite de utilizator (clase, structuri) trebuie sa exista un constructor de copiere, altfel compilatorul va genera o eroare.

In al doilea exemplu, intregul este pasat lui foo() prin referinta, cea ce inseamna ca modificarea lui in foo inseamna modificarea directa a obiectului original din mai. Programul va afisa 0 1 1:

Code: Select all

void foo(int &i)
{
  i++;
  cout << i << endl;
}

int main()
{
  int i = 0;

  cout << i << endl;
  foo(i);
  cout << i << endl;
  
  return 0;
}
Pe de alta parte, se poate transmite un pointer la intreg. Pointerul insusi se transmite prin copiere, dar avand adresa obiectului din main, acesta poate fi modificat direct din foo. Asftfel ca programul va afisa din nou 0 1 1:

Code: Select all

void foo(int *i)
{
  (*i)++;
  cout << *i << endl;
}

int main()
{
  int i = 0;

  cout << i << endl;
  foo(&i);
  cout << i << endl;
  
  return 0;
}
Cand o functie primeste un pointer, se poate intampla ca cineva sa transmita NULL, astfel incat e recomandabil sa se testeze acest caz inaintea folosirii pointerului respectic.

In cazul in care se doreste transmiterea unui pointer la un buffer unei functii care sa aloce memorie, codul de mai jos nu va functiona:

Code: Select all

// ACEST COD ESTE GRESIT
void foo(unsigned char* buffer)
{
  // sterge bufferul existent
  if(buffer != NULL)
    delete [] buffer;

  // aloca memorie
  buffer = new unsigned char[2];

  // scrie in memoria nou alocata
  buffer[0] = 0;
  buffer[1] = 1;
}

int main()
{
  unsigned char * buffer = NULL;

  foo(buffer);

  delete [] buffer;
  
  return 0;
}
Adresa buffer-ului este transmisa prin copiere. Functia foo nu face decat sa altereze pointer-ul pe de stiva sa, nu cel din functia main. Pentru aceasta, respectivul pointer trebuie trimis prin referinta:

Code: Select all

void foo(unsigned char* &buffer)
{
  // sterge bufferul existent
  if(buffer != NULL)
    delete [] buffer;

  // aloca memorie
  buffer = new unsigned char[2];

  // scrie in memoria nou alocata
  buffer[0] = 0;
  buffer[1] = 1;
}

int main()
{
  unsigned char * buffer = NULL;

  foo(buffer);

  delete [] buffer;
  
  return 0;
}
In acest caz functia foo() va altera direct pointerul declarat in functia main().



Next FAQ >>