Przesłanie tablicy do funkcji

0

Witam oglądałem sobie filmik Mirosława Zelenta teraz i zastanawiają mnie dwie rzeczy obie dotyczą funkcji.
Np jak mam funkcje z takimi deklaracjami:

void przyklad1(int &a,int &b);
void przyklad2(int *a,int *b);

To w przykladzie 1 wywoluje funkcje podajac zmienne bez używania referencji,a w przykladzie 2,podaje inaczej.Głownie chodzi mi o to czego funkcja oczekuje przy jej wywołaniu?Jak w deklaracji jest referencja to podaje tylko zmienną,a jak w deklaracji jest wskaźnik to podaje wskaźnik.Trochę nielogiczne to dla mnie jest.Jak się ja domyślam to referencja jest znacznie prostsza do przeslania i kompilator łatwo to zamieni,a wskaźniki to ja musze definiować,ale jakoś kupy się to nie trzyma.

Drugie pytanie to:

void przyklad1(int *tab,int size);
void przyklad2(int tab[],int size);

Po co w przykladzie 1 dawać wskaźnik do tablicy,jak można dać tak jak w przykładzie drugim ,jak i tak ja do funkcji podaje adres zerowej komórki tablicy?

Może pierwsze pytanie zadałem troche chaotycznie,ale nie potrafie słownie przekazać tych moich wątpliwości.Nie widze liniowości w przekazywaniu parametrów do funkcji.

0

Dobra, czym się różni przekazywanie przez referencję od przekazywania przez wskaźnik. Na pierwszy rzut oka niczym. Przy okazji rozwieję Twoje pierwsze wątpliwości o przekazywaniu przez wskaźnik. Niczego nie musisz definiować:

 
void foo1(int & a)
{
  a = 5;
}

void foo2(int * a)
{
   *a = 5
}

void bar()
{
  int x = 0;
  foo1(x);

  int y = 0;
  foo2(&y);
}

W tym przykładzie zarówno x jak i y będą miały na koniec wartość 5. Zobacz, jak przekazałem y do funkcji foo2. Nie deklarowałem żadnego wskaźnika.

W tym przypadku nie ma żadnej różnicy w działaniu. W nowoczesnym programowaniu zaleca się stosowanie referencji zamiast wskaźników, bo są bardziej czytelne. No i jakieś takie bardziej przyjazne. Ale są sytuacje, w których nie da się bez wskaźnika.

Załóżmy taki przykład, że masz jakąś klasę. Nazwijmy ją Connector. Nie jest ważne, co robi. Ale ważne jest to, że obiektu tej klasy nie możesz tworzyć sam. Musi być utworzony przez jakąś inną, prawdopodobnie zewnętrzną funkcję. Teraz taka funkcja może mieć dwojaką postać, np:

 
Connector * CreateConnector()
{
  return new Connector();
}

//albo np:
bool CreateConnector(Connector *& pObj)
{
  try
  {
    pObj = new Connector();
    return true;
  }catch(...)
  {
    return false;
  }
}

Coś się tu może nie skompilować, bo pisałem na sucho. Pierwszy przykład wiadomo jak działa, nie ma co omawiać. W drugim przykładzie musisz przekazać wskaźnik do referencji. Czasami robi się wskaźnik na wskaźnik: (Connector ** ppConnector).

Różnica w wywołaniu jest taka:

Connector *pC1 = CreateConnector();

Connector * pC2;
if(CreateConnector(pC2)) 
{
  //
}

Generalnie zawsze stosuj referencje, chyba że naprawdę musisz mieć wskaźnik z jakiegoś powodu.

Co do tablic. Przede wszystkim nie stosuj tablic. Staraj się ich unikać. Zamiast nich, stosuj odpowiedni kontener z STL, np. vector.
Ale, żeby dopełnić tematu.

Nie ma tak naprawdę różnicy, czy przekażesz tablicę przez wskaźnik, czy przez []. Tzn. w pierwszym przypadku przekażesz po prostu wskaźnik. W drugim przekażesz referencję do tablicy. W C++ z tego, co pamiętam, nie można przekazać tablicy przez wartość.

Oczywiście inaczej odnosisz się do tablicy przekazywanej przez wskaźnik, np:

void foo(int * pTab, int size)
{
  int * pItem = pTab;
  //dalej możesz sobie już patrzeć na kolejne elementy tablicy, po prostu inkrementując wskaźnik:
  ++pItem;
}

Ale uwaga! Tu jest niebezpieczeństwo. Jeśli będziesz posługiwał się wskaźnikiem pTab, np:
pTab++
to wtedy popsujesz sobie całą tablicę. Bo Twój wskaźnik na tablicę nie będzie już wskazywał na jej pierwszy element, tylko kolejny. Prosta droga do problemów z pamięcią i wycieków.</del>

0

Oczywiście inaczej odnosisz się do tablicy przekazywanej przez wskaźnik, np:

void foo(int * pTab, int size)
{
  int * pItem = pTab;
  //dalej możesz sobie już patrzeć na kolejne elementy tablicy, po prostu inkrementując wskaźnik:
  ++pItem;
} 

Nie ma znaczenia jak przekażesz tablicę do funkcji.

void przyklad1(int *tab,int size);
void przyklad2(int tab[],int size); 

W obu przypadkach korzystasz ze wskaźnika na początek tablicy (tablica jest castowana na wskaźnik do jej adresu zerowego).

Zapis tablicowy i wskaźnikowy wobec tablicy możesz używać zamiennie. Są sobie równoważne.

#include <iostream>

void foo(int * pTab, int size)
{
    for (int i = 0; i < size; ++i) {
        std::cout << pTab[i] << ' ';
    }
    std::cout << '\n';
    for(int i = 0; i < size; ++i) {
        std::cout << (*(pTab + i)) << ' ';
    std::cout << '\n';
}

void foo2(int tab[], int size)
{
    int* pItem = tab;
    for (int i = 0; i < size; ++i) {
        std::cout << *(pItem++) << ' ';
    }
    std::cout << '\n';
}

int main()
{
    constexpr int size = 5;
    int tab[size]{ 1,2,3,4,5 };
    foo(tab, size);
    foo2(tab, size);
} 

Dużo lepszym i bezpieczniejszym rozwiązaniem jest korzystanie z std::array.

1 użytkowników online, w tym zalogowanych: 0, gości: 1