Funkcja, przesłanie **wsk, nie działa, new, tablica

0

Witam
Mam pewien problem i nie mam pojęcia jak go rozwiązać. Otóż przesyłam do funkcji **wsk, przydzielam mu tablice po czym nie mogę jej użyć w main.

 
#include <iostream>
using namespace std;

void utworz(int **wsk, int N)
{
    wsk = new int*[N];
    for(int i = 0; i < N; i++)
    {
        wsk[i] = new int[N];
    }
}
//--------------------------------
int main()
{
    int **wsk;
    int N = 5;

    utworz(wsk, N);

    cout << wsk[0][0];
}
1

Bo za mało o jedną gwiazdkę (albo referencje) dajesz. W C w zasadzie wszystko jest przekazywane przez wartość, dotyczy to też wskaźników. Tak więc Twój wskaźnik do wskaźnika jest kopiowany, jego wskazanie zmieniane na coś nowego co zwraca new, ale wskaźnik z main wskazuje dalej na to samo na co wskazywał przed wykonaniem funkcji. Żeby to działało dodaj albo jedną gwiazdkę więcej, albo referencję, albo zwracaj ten wskaźnik z funkcji.

Zauważ, że problem byłby od razu widoczny, gdybyś zrobił tak:

typedef int ** ArrayPointer;

Funkcja będzie wtedy taka:

void utworz(ArrayPointer wsk, int N);

Widzisz teraz, że brakuje gwiazdki albo referencji?

1

Do funkcji przekazałeś KOPIĘ tego wskaźnika, bo argumenty funkcji zawsze są zmiennymi lokalnymi!
Materiał poglądowy:

#include <iostream>
using namespace std;
 
void funkcja1(int argument)
{
  argument = 1;
}

void funkcja2(int& referencja)
{
  referencja = 2;
}

void funkcja3(int* wskaznik)
{
  *wskaznik = 3;
}

int main()
{
  int dana = 0;
  funkcja1(dana); //
  cout<< dana; //nadal wynosi 0 bo wewnatrz funkcji zmienialiśmy tylko lokalną kopię!
  funkcja2(dana);
  cout<< dana; // 2 bo wewnatrz funkcji zmienialiśmy oryginał zmiennej, bo przekazaliśmy tam referencje!
  funkcja3(&dana);
  cout<< dana; // 3 bo wewnatrz funkcji dostaliśmy adres do miejsca w pamięci gdzie leży nasza dana i zmieniliśmy wartość w tym miejscu w pamięci
  return 0;
}
0
Shalom napisał(a)

Do funkcji przekazałeś KOPIĘ tego wskaźnika, bo argumenty funkcji zawsze są zmiennymi lokalnymi!
Materiał poglądowy:

#include <iostream>
using namespace std;
 
void funkcja1(int argument)
{
  argument = 1;
}

void funkcja2(int& referencja)
{
  referencja = 2;
}

void funkcja3(int* wskaznik)
{
  *wskaznik = 3;
}

int main()
{
  int dana = 0;
  funkcja1(dana); //
  cout<< dana; //nadal wynosi 0 bo wewnatrz funkcji zmienialiśmy tylko lokalną kopię!
  funkcja2(dana);
  cout<< dana; // 2 bo wewnatrz funkcji zmienialiśmy oryginał zmiennej, bo przekazaliśmy tam referencje!
  funkcja3(&dana);
  cout<< dana; // 3 bo wewnatrz funkcji dostaliśmy adres do miejsca w pamięci gdzie leży nasza dana i zmieniliśmy wartość w tym miejscu w pamięci
  return 0;
}

Doprawdy zawsze ?

 
#include <iostream>
using namespace std;
//***********************************
void funk(int *wsk)
{
    *wsk = 5;
}
//***********************************
int main()
{
    int *wsk = 0;
    int liczba = 10;

    wsk = &liczba;

    funk(wsk);

    cout << liczba << " " << *wsk << endl;
}

Z tego własnie powodu nie mogę sobie wyobrazić jak wskaźnik można przekazać jako wartość. Nawet w symfonii jest napisane że wskaźnik z referencja jest wymienne.

Byłyby ktoś w stanie to tak poprawić żeby działało próbowałem już na każdy sposób. A jeżeli się uda to napisał dlaczego tak.

#include <iostream>
using namespace std;
//***************************
void funk(int *wsk)
{
    wsk = new int(5);
}
//***************************
int main()
{
    int *wsk = 0;

    funk(wsk);

    cout << *wsk << endl;
}

 
1

Nie rozumiem o czym piszesz. Pokazałeś przykład identyczny jak ten mój przecież. Zmieniłeś w funkcji wartość w miejscu pamięci na które pokazywał wskaźnik, a nie kopię wskaźnika którą przekazano do funkcji. Co by się stało jakby wewnatrz tej funkcji zrobił na przykład:
wsk = 0 (bez żadnych dereferencji!)
? Ano nic by się nie stało, bo poza funkcją wskaźnik nadal pokazywałby na to samo bo zmieniłbyś tylko wskazanie tej lokalnej kopii...

0

Ok już rozumiem w jaki sposób się tak dzieje. W funkcji tworzony jest nowy wskaźnik który pokazuje na adres wskaźnika z main(). Po utworzeniu obiektu (new int) zmienia on adres i nie ma związku z wskaźnikiem z maina(). Czyli w jaki sposób można utworzyć obiekt dynamiczny w funkcji? Domyślam się ze tylko poprzez return.
Np. wsk = funk();

0

Jak byś był w stanie napisz mi to w kodzie. Wtedy wszystko się już dla mnie wyjaśni.

1

Nie rozumiem? Przecież masz przykłady! Umiesz zmienić wartość inta przekazanego przez wskaźnik, nie umiesz tego zrobić analogicznie? Skoro chcesz zmienić wartość
int* który na coś pokazuje to do funkcji musisz przekazać albo int** albo int*&

0

No własnie robię tak i nie działa. Przecież jak by mi się udało to bym Ciebie o to nie prosił :)
O ten przykład mi chodzi.

 
#include <iostream>
using namespace std;
//***************************
void funk(int *wsk)
{
    wsk = new int(5);
}
//***************************
int main()
{
    int *wsk = 0;
 
    funk(wsk);
 
    cout << *wsk << endl;
}
 
1

Przepraszam ale której części mojej wypowiedzi nie rozumiesz? Napisałem że jeśli chcesz zmienić int* to musisz przekazać int** albo int*& a ty tego nie robisz...

#include <iostream>
using namespace std;

void funk(int ** wsk)
{
    *wsk = new int(5);
}

int main()
{
    int *wsk = 0;
    funk(&wsk); //przekazujemy adres w pamięci gdzie jest wskaźnik który chcemy zmienić
    cout << *wsk << endl;
}

albo tak

#include <iostream>
using namespace std;

void funk(int*& wsk)
{
    wsk = new int(5);
}

int main()
{
    int *wsk = 0;
    funk(wsk); //przekazujemy referencje do wskaźnika
    cout << *wsk << endl;
}

Druga opcja wygląda trochę czytelniej, ale jest ryzykowna -> nie widać na pierwszy rzut oka, w miejscu wywołania, że wartość argumentu może ulegać zmianie.

1

Postanowiłem narysować:

ptrs.png

Po lewej Twój kod, po prawej Shaloma z podwójnym wskaźnikiem.

1

poprawka minimalna twojego kodu to:

void utworz(int **&wsk, int N)
1

void funk(int*& wsk)

zazwyczaj piszę w ten sposób:

int* &wsk

wtedy lepiej widać intencję, że wsk jest referencją (&) na int*

albo ze wskaźnikiem

int* *wsk
  • wskaźnik na int*, w przeciwieństwie do
int **wsk
int** wsk
  • wskaźnik na wskaźnik.

Dla kompilatora to jest oczywiście to samo, dla nas nie zawsze.

0

funk(&wsk); //przekazujemy adres w pamięci gdzie jest wskaźnik który chcemy zmienić

Tutaj jestem zaskoczony. Nie wpadł bym ze można adres wskaźnika przesłać :) Całą swoją wiedzę opieram tylko o symfonie a tam takich cudów nie ma. Teraz już czaje wszystko. Wielkie dzięki.

W sumie to jeszcze jedno pytanie. Czemu jak tworzysz operatorem new jest *wsk = new int(5); a nie wsk = new int(5)? Oraz czy jak w main() użyje delete wsk; nie będzie wycieków pamięci.

Ok rozwiałem swoje wątpliwości. Na pierwszy kontakt z tym jest to bardzo zawiłe. Dla utrwalenia wydrukuje i przykleję na ścianie nad biurkiem :)

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