Wskaźnik do zmiennej lokalnej nie działa zgodnie z oczekiwaniami

0

Witam wszystkich serdecznie.

Przerabiam aktualnie wskaźniki, a dokładniej przekazywanie wskaźników do funkcji oraz zwracanie przez funkcję wskaźnika i mam pewien problem związany ze zrozumieniem działania, a oto kod:

#include <iostream>
#include <stdlib.h>

using namespace std;

int* test_no_malloc(int* x, int* y);
int* test_malloc(int*x, int* y);

int main()
{
    int a=1, b=3;
    int* ptr_no_malloc = test_no_malloc(&a, &b);
    cout << "test1 bez malloca!" << endl << "WARTOSC: " << *ptr_no_malloc << endl << "ADRES: " << &ptr_no_malloc << endl << endl;
    cout << "test2 bez malloca!" << endl << "WARTOSC: " << *ptr_no_malloc << endl <<  "ADRES: " << &ptr_no_malloc << endl << endl;
    cout << "test3 bez malloca!" << endl << "WARTOSC: " << *ptr_no_malloc << endl << "ADRES: " << &ptr_no_malloc << endl << endl;

    int* ptr_malloc = test_malloc(&a, &b);
    cout << "test1 bez malloca!" << endl << "WARTOSC: " << *ptr_malloc << endl << "ADRES: " << &ptr_malloc << endl << endl;
    cout << "test2 bez malloca!" << endl << "WARTOSC: " << *ptr_malloc << endl << "ADRES: " << &ptr_malloc << endl << endl;
    cout << "test3 bez malloca!" << endl << "WARTOSC: " << *ptr_malloc << endl << "ADRES: " << &ptr_malloc << endl << endl;

    free(ptr_malloc);
    return 0;
}

int* test_no_malloc(int* x, int* y)
{
    int no_malloc = (*x) + (*y);
    return &no_malloc;
}

int* test_malloc(int *x, int *y)
{
    int* ptr_malloc = (int*)malloc(sizeof(int));
    *ptr_malloc = (*x) + (*y);
    return ptr_malloc;
}

Pytanie brzmi:

  1. Dlaczego jeśli zwracam wskaźnik bez użycia malloca, to po wyjsciu z funkcji tylko pierwsze wywolanie podaje prawidłową wartość, mimo iż adresy są identyczne(domyślam się, że wartości już nie ma pod adresem wskaźnika, ale dlaczego?), natomiast gdy używamy malloca, problemu nie ma.
  2. Czy jest jakiś sposób aby było poprawnie bez użycia malloca?

Prosze o wyrozumiałośc, gdyż jestem podczas nauki o wskaźnikach.

Pozdrawiam :)

0

W test_no_malloc zwracasz adres zmiennej lokalnej, która przestaje istnieć po zakończeniu wywołania funkcji

2

Zwracasz wskaźnik do zmiennej lokalnej. Nie możesz tego robić, to zawsze UB. Zamiast tego możesz po prostu zwracać obliczoną wartość.

0

To jak zwrócić wskaźnik z funkcji bez użycia malloca ?

0

Musisz zwrócić wskaźnik do czegoś, co istnieje. Odpowiedz sobie lepiej na pytanie: co chcesz zrobić?

0

Właśnie nie chcę zwracać wartości ;( Chcę nauczyć się prawidłowo zwracać wskaźnik z funkcji.

1

Musisz zwrócić wskaźnik do obiektu, którego czas życia przekracza czas życia funkcji. Jest wiele sposobów aby to zagwarantować, ale zależnie od tego co chcesz zrobić są one poprawne lub nie. Nie ma magicznego sposobu na zawsze poprawne "zwracanie wskaźnika z funkcji".

0

Generalnie robie zadanie z książki Szkoła Programowanie i jest tam zadanie, aby przerobić kod:

#include <iostream>

const int Max = 5;

int fill_array(double ar[], int limits);
void show_array(const double ar[], int n);
void revalue(double r, double ar[], int n);

int main()
{
    using namespace std;
    double properties[Max];

    int size = fill_array(properties, Max);
    show_array(properties, size);
    cout << "Podaj czynnik zmiany wartosci: ";
    double factor;
    cin >> factor;
    revalue(factor, properties, size);
    show_array(properties, size);
    cout << "Gotowe." << endl;
    return 0;
}

int fill_array(double ar[], int limit)
{
    using namespace std;
    double temp;
    int i;
    for(i=0; i<limit; i++)
    {
        cout << "Podaj wartosc nr " << (i+1) << ": ";
        cin >> temp;
        if(!cin)
        {
            cin.clear();
            while(cin.get() != '\n')
                continue;
            cout << "Bledne dane, wprowadzanie danych przerwane.\n";
            break;
        }
        else if(temp < 0)
            break;
        ar[i] = temp;
    }
    return i;
}

void show_array(const double ar[], int n)
{
    using namespace std;
    for(int i=0; i<n; i++)
    {
        cout << "Nieruchomosc nr " << (i+1) << ": ";
        cout << ar[i] << endl;
    }
}

void revalue(double r, double ar[], int n)
{
    for(int i=0; i<n; i++)
        ar[i] *= r;
}

Treśc zadania:
Zmodyfikuj program zmieniając trzy funkcje obsługujące tablice tak, aby każda z nich używała dwóch wskaźników określających zakres. Funkcja fill_array(), zamiast zwracać aktualną liczbę elementów, ma zwrócić wskaźnik elementu znajdującego się za ostatnim wypełnionym elementem. Inne funkcje mają używać tego wskaźnika jako drugiego parametru pozwalającego wykryć koniec danych.

Dlatego właśnie zależy mi na tym prawidłowym zwracaniu wskaźnika, a nie wartości ;)

0
morti napisał(a):

To jak zwrócić wskaźnik z funkcji bez użycia malloca ?

Zacznijmy od początku. Mini poradnik dla opornych ;)

Wskaźniki:

  1. Dane są trzymane w pamięci, może to być np. stos, sterta, pamięć globalna, zamapowana, etc.
  2. Wskaźnik to adres (liczba) identyfikująca jednoznacznie lokalizację danych w pamięci.
  3. Żeby użyć wskaźnika musisz być pewien, że pod adresem pamięci, na który wskazuje są odpowiednie dane, a przede wszystkim musi być to prawidłowy, istniejący dla procesu adres.

Stos:
4. Jest używany do przetrzymywania rekordów aktywacji (wywołań funkcji). To na nim znajdują się zmienne lokalne.
5. Wywołanie funkcji tworzy nowy rekord aktywacji wraz ze zmiennymi.
6. Wyjście z funkcji sprawia, że rekord aktywacji jest nieważny, tj. zdjęty ze stosu. Zaraz zostanie nadpisany przez inny rekord przy okazji wywołania innej funkcji.

Sterta:
7. Używasz, gdy alokujesz pamięć przez new, malloc, calloc, realloc.
8. Zaalkowana pamięć jest ważna, tzn. nie zostanie przydzielona na inne dane, dopóki sam jej nie usuniesz.

Zwracając wskaźnik do zmiennej lokalnej na stosie, zwracasz wskaźnik na pamięć, która została już unieważniona i mogła zostać nadpisana innymi danymi np. przez procedurę wypisywania na ekran.

PS.
Są języki programowania, które wykonują tak zwaną escape analysis i mogą dane ze stosu przenieść na stertę już podczas kompilacji (np. golang).

0

Dzięki wielkie za wyjaśnienia na czym polegał problem ;) Czas dajel walczyć.

0

Zadanie zrobione, ale nie wiem czy o to chodziło. Jeśli będzie ktoś tak uprzejmy i sprawdzi czy to autor miał na myśli odnośnie przerobienia programu na wskaźniki?
Dla ułatwienia jeszcze raz napiszę treść zadania, do kodu powyżej:

Zmodyfikuj program zmieniając trzy funkcje obsługujące tablice tak, aby każda z nich używała dwóch wskaźników określających zakres. Funkcja fill_array(), zamiast zwracać aktualną liczbę elementów, ma zwrócić wskaźnik elementu znajdującego się za ostatnim wypełnionym elementem. Inne funkcje mają używać tego wskaźnika jako drugiego parametru pozwalającego wykryć koniec danych.

Oto kod, który u mnie działa:

#include <iostream>
#include <cstdlib>

int Max = 5;

double* fill_array(double ar[], int* wsk);
void show_array(const double ar[], double* wsk);
void revalue(double r, double ar[], double* wsk);

int main()
{
    using namespace std;
    double properties[Max];
    int* wsk_do_funkcji = &Max;

    double* wsk_z_funkcji = fill_array(properties, wsk_do_funkcji);

    show_array(properties, wsk_z_funkcji);

    cout << "Podaj czynnik zmiany wartosci: ";
    double factor;
    cin >> factor;

    revalue(factor, properties, wsk_z_funkcji);
    show_array(properties, wsk_z_funkcji);
    cout << "Gotowe." << endl;

    return 0;
}

double* fill_array(double ar[], int* wsk)
{
    using namespace std;
    double temp;
    double* ptr;
    int i;
    for(i=0; i<*wsk; i++)
    {
        cout << "Podaj wartosc nr " << (i+1) << ": ";
        cin >> temp;
        if(!cin)
        {
            cin.clear();
            while(cin.get() != '\n')
                continue;
            cout << "Bledne dane, wprowadzanie danych przerwane.\n";
            break;
        }
        else if(temp < 0)
            break;
        ar[i] = temp;
    }
    ptr = &ar[i];
    return ptr;
}

void show_array(const double ar[], double* wsk)
{
    using namespace std;
    int i=0;
    while(wsk!=&ar[i])
    {
        cout << ar[i] << endl;
        i++;
    }
}

void revalue(double r, double ar[], double* wsk)
{
    int i=0;
    while(wsk!=&ar[i])
    {
        ar[i] *= r;
        i++;
    }
}

Pozdrawiam :)

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