Operacje na wskaźnikach

0

Cześć, mam taką funkcję, która zwraca wskaźnik do tablicy, jej argumentami są również wskaźniki do tablic:

int* magic(int *l1, int *l2)

Jeśli wywołam tę funkcję dla zdefiniowanych argumentów to otrzymuję wskaźnik do wyniku:

*wynik = magic(jeden, dwa);

Następnie wywołuję ponownie funkcję dla wyniku oraz trzeciej tablicy:

*wynik = magic(*wynik, trzy)

W tym momencie otrzymuję wynik całkowicie niezgodny z oczekiwaniami. W jaki sposób powinienem wywoływać funkcję magic? Średnio ogarniam sprawę wskaźników, więc pewnie w tym leży problem.

0

Pokaż funkcje magic.

0

W funkcji magic wykonywane są operacje na elementach l1 i l2: wyniki tych operacji wpisywane są do tablicy wynik.

int* magic(int *l1, int *l2)
{
    int wynik[256] = {0,};
    
    // l1i l2 nie sa modyfikowane
    // wewnątrz funkcji do l1, l2, wynik odwołuję się po prostu l1, l2, wynik. Nie *l1, *l2, *wynik.

    return wynik;
}

Nie wiem czy powinienem zwracać *wynik czy wynik?

2

Ani jedno ani drugie. To jest po prostu źle. Teraz zwracasz z funkcji wskaźnik do lokalnej tablicy która po wyjściu z funkcji może zostać zamazana. Zwrócenie *wynik też nic nie da bo to zwróci ci tylko pierwszy element tablicy. Musisz zwrócić albo całą tablicę albo alokować pamięć dynamicznie.

0

Mogę w takim razie prosić o przykład prawidłowo napisanej funkcji wraz z wywołaniem, której argumentami są dwie tablice (czy tam wskaźniki) i która zwraca trzecią tablicę?
Tak, że możliwe jest wywołanie w stylu:

wynik = funkcja(tab1, tab2);

a wynik może być wykorzystany później również jako jeden z argumentów funkcji.

0

poczytaj o new/delete lub malloc/free

0
#include <iostream>
using namespace std;

int* magic(int *l1, int *l2)
{
    int* wynik = new int[4];
    wynik[0]=l1[0]+1;
    wynik[1]=l2[0]-1;
    return wynik;
}

void wypisz(int* tab){
    for (int i=0;i<4;i++){
        cout<<tab[i]<<" ";
    }
    cout<<endl;
}

int main(){

    int t1[]={1,2,3,4};
    int t2[]={5,6,7,8};
    int* t3 = magic(t1,t2);
    wypisz(t3);
    int* t4 = magic(t3,t1);
    wypisz(t4);

    delete[] t3;
    delete[] t4;
    return 0;
}

0

Mogłem wyraźniej zaznaczyć, że piszę w C nie w C++, moja wina.
Dam sobie radę z przepisaniem new na malloc. Dzięki Shalom!

0

Radzę to zrobić jednak w inny sposób:

int *magic(int *l1,int *l2,int *wynik)
  {
   wynik[0]=l1[0]+1;
   wynik[1]=l2[0]-1;
   return wynik;
  }

int main()
  {
   int t1[]={1,2,3,4},t2[]={5,6,7,8},t3[4],t4[4];
   magic(t1,t2,t3);
   wypisz(t3);
   magic(t3,t1,t4);
   wypisz(t4);
   return 0;
  }
0

Też na początku kombinowałem w ten sposób, jednak zależało mi na tym, żeby była to typowa funkcja - przyjmuje tylko dane wejściowe i zwraca wynik.
Co do malloc. W którym momencie muszę użyć free? Czy przy powiedzmy setkach wywołań malloc będzie zajmował kolejne sektory pamięci bez zwalniania nieużywanych już zmiennych?
Powiedzmy, że iteracyjnie liczę silnię, a moja funkcja to funkcja mnożenia:

while (n < MAX)
{
	silnia = iloczyn(silnia, ++n);
}

Czy taki program zachowuje się w sposób, który opisałem powyżej?

0

@Shalom nie do końca fajnie odpowiedział. Otóż jeśli już sami zarządzamy alokacją pamięci to ekstremalnie ważne jest aby robić to w spójny i logiczny sposób. Dlatego nie powinny powstawać kody-kwatki w których jedna funkcja coś alokuje, a inna, bliżej niepowiązana funkcja to coś zwalnia (oczywiście są nieliczne wyjątki). Dlatego proponuję bez bawienia się w żadne alokacje, pierdoły i inne. Można sobie poradzić z problemem zaskakująco prosto i skutecznie na 2 sposoby:

  1. Zrobić z tablicy tablicę statyczną.
    Tablica będzie wtedy jedna, globalna dla całego programu i będzie istnieć aż do jego zamknięcia. Wiele by można zarzucić takiemu trickowi, ale w C jest to często spotykana praktyka i nie powinna nikogo zaskoczyć. Wadą jest to, że funkcja wtedy staje się nieco upośledzona - nie można z niej korzystać równolegle (wątki) a wynik zwrócony jest ważny tylko do następnego wywołania funkcji.

  2. Przekazać, jako dodatkowy parametr, tablicę do której wpisać wynik.

/* ta funkcja zadziała wszędzie, ale jest trochę mniej wygodna w użyciu */
char *JakasFunkcja(char tablica_na_wynik[], size_t rozmiar_tablicy)
{
    /* ... tu wypełniamy wynik ... */
    tablica_na_wynik[i] = ...

    return tablica_na_wynik;
}

/* ta funkcja jest prosta, ale ma wspomniane wady */
char *JakasFunkcja()
{
    static char tablica_na_wynik[256];
    return JakasFunkcja(tablica_na_wynik, sizeof(tablica_na_wynik));
}
0

A w zasadzie można połączyć obydwa i w ten sposób uzyskać prostotę pierwszego i funkcjonalność drugiego:

Oraz zupełnie nierozwiązany problem, bo znowu w tej drugiej funkcji zwracasz tablicę ze stosu :D.

edit: no, brakowało static, ale widzę, że szybki edit naprawił niedopatrzenie ;).

0

Skupiłem się na zarządzaniu pamięcią, ale jest jeszcze jedna rzecz, w zasadzie ważniejsza.

Jeśli będziesz chciał użyć jednej i tej samej tablicy zarówno do przekazania parametru do funkcji oraz do zwrócenia wyniku, twoja funkcja musi być na to przygotowana - wpisując znak w komórkę wyniku jednocześnie niszczysz oryginalną wartość w niej zapisaną (przekazany parametr). Tak więc po wpisaniu znaku w i'tą komórkę nie możesz odczytywać już i'tej komórki parametru.

0

@adf88 poważnie uważasz że twoje rozwiązanie jest lepsze? o_O Myślałem nawet żeby napisać autorowi o tym że może użyć statycznej tablicy w funkcji, ale uznałem że to jest tak niepraktyczne i średnio-intucyjne że lepiej tego nie robić bo zaraz znów przybiegnie na forum pytać czemu to nie działa ;]
W czym problem? W tym że możesz w danej chwili mieć tylko jeden "wynik" takiej funkcji co jest dość dziwnym pomysłem. Szczególnie kiedy autor chciał wykonywać:

wynik = magic(jeden, dwa);
wynik = magic(wynik, trzy)

Czyli chce do tej funkcji wysłać jako parametr poprzedni wynik. To oznacza że to drugie wywołanie funkcji zrobi misz-masz i na pewno nie zadziała tak jak autor by tego chciał.
Alokacja pamięci w tej funkcji jest szalona i na 100% będzie skutkować wyciekami pamięci, ale przynajmniej będzie działać tak jak powinno.

0

Dzięki wszystkim za pomoc, nie spodziewałem się, że tak poszerzę swoją wiedzę ;)
Ostatecznie do funkcji dodałem trzeci parametr określający miejsce zapisania wyniku. Zamiast jednak manipulować tym wskaźnikiem tworzę tablicę pomocniczą i na samym końcu wykorzystując memcpy kopiuję zawartość pomocniczej tablicy do tablicy docelowej.
W ten sposób wywołanie:

while (n < MAX)
{
	iloczyn(silnia, ++n, silnia);
}

nie stwarza problemów.

0
Shalom napisał(a):

@adf88 poważnie uważasz że twoje rozwiązanie jest lepsze? o_O
No tak, pewnie, że tak. Przede wszystkim w nauce programowania nie liczy się tylko rozwiązywanie problemów dowolnie wybraną drogą. Liczy się też nauka dobrych praktyk. Samego Cibie pewnie nie raz trafiał szlak widząc noobk'a który po milionach copy-paste'ów "poradził sobie" w jakiś szalony i kompletnie błędny sposób i mówi, że ten jego kod jest "dobry" bo działa. Lepiej jak ktoś się przyłoży czasem lepiej niż przystanie na WTF'owy kod.

Co do niezbyt wygodnego i zawierającego pułapki bufora statycznego - i tak jest to sto razy lepsze niż alokacja wewnątrz funkcji. Nie rozpieprza zarządzania pamięcią i jest właściwie tylko opakowaniem na wersję bez bufora którą możemy użyć alternatywnie. IMO dzikie zarządzanie pamięcią to dużo bardziej niebezpieczna rzecz. No i jeśli chodzi o C-like kody taka praktyka ze statycznym buforem jest chyba popularna (a przynajmniej ja nie raz z nią się spotkałem).

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