Iteracja po dynamicznie alokowanej tablicy

Iteracja po dynamicznie alokowanej tablicy
SB
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:4
0

Witam, prosiłbym o naprowadzenie mnie na poprawny tok rozumowania. Mianowicie, mam poniższą funkcję:

Kopiuj
int * apply_all(const int * const arr1, size_t size1, const int * const arr2, size_t size2){
	int* arr3 = new int[size1*size2];
	for (size_t i{ 0 }, k{ 0 }; i < size1; i++) {
		for (size_t j{ 0 }; j < size2; j++, k++) {
			arr3[k] = arr1[i] * arr2[j];
		}
	}
	return arr3;
}

I kombinuję teraz nad modyfikacją jej, a dokładniej chodzi mi sposób iterowania po arr3. Jak widać, używam zmiennej k, ale chciałbym zrobić to jakoś za pomocą notacji offsetowej i wpadłem na pomysł użycia:

Kopiuj
*arr3++

Tylko oczywiście dostaje jakieś śmieciowe wyniki. W takim razie arr3 nie posiada początkowego adresu tablicy na stercie? To co znajduje się w arr3 po zainicjalizowaniu?

c7
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad rok
  • Postów:89
0

Tylko oczywiście dostaje jakieś śmieciowe wyniki.

Odpowiedź znajduje się tutaj:
Jaka operacja zostanie wykonana jako pierwsza w przypadku: *arr3++ ?
(*arr3)++ czy *(arr3++)

W takim razie arr3 nie posiada początkowego adresu tablicy na stercie

Na samym początku tak, ale jeśli potem inkrementujesz pointer (czyli go przesuwasz), to nie.

Edit:

Możesz zrobić pomocniczy pointer do iteracji po tablicy i koniecznie zwracać ten, który pointuje na początek.

edytowany 2x, ostatnio: c7
TomaszLiMoon
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 8 godzin
  • Postów:530
2

Używając

Kopiuj
*arr3++

po zakończeniu pętli, zwracasz wskaźnik pokazujący adres znajdujący się za adresem ostatniego elementu tablicy.
Czytanie wartości z poza zakresu powoduje wyświetlanie różne śmieci, a zapisywanie prowadzi do UB.

Jeżeli chcesz używać tej notacji to powinieneś zapisać adres tej tablicy.

Kopiuj
int * apply_all(const int * const arr1, size_t size1, const int * const arr2, size_t size2){
    int* const arr3 = new int[size1*size2];
    int* iter = arr3;
    for (int i=0 ; i < size1; ++i) {
        for (int j=0 ; j < size2; ++j) {
            *iter++ = arr1[i] * arr2[j];
        }
    }
    return arr3;
}

Lepszym rozwiązanie jest tutaj użycie vectora, gdyż nie musisz się w tym przypadku zamartwić zwalnianiem i przydzielaniem pamięci oraz pilnowaniem zakresów tablic, co w dłuższej perspektywie uchroni cię od wielu nieprzyjemnych błędów.

Kopiuj
vector<int> multiply( const vector<int>& arr1 , const vector<int>& arr2 )
{
    vector<int> result;

    for( const auto& element1 : arr1 )
    {
        for( const auto& element2 : arr2 )
        {
            result.push_back(element1*element2);
        }
    }

    return result;
}
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:mniej niż minuta
5

A ja proponuje porzucić C na rzecz C++ i pisać kod bez cudów typu const int * const arr1 new int[size1*size2]; (twój kod używa C++, ale jest pisany tak jakby to było C).
Po to masz std::vector std::array std:span (c++20), żeby z nich korzystać.
Albo jeszcze lepiej zamknąć tą funkcjonalność w jakiejś klasie lub szablonie, żeby się z tego łatwo korzystało.

Polecam lekturę: https://dsp.krzaq.cc/post/176/ucze-sie-cxx-kiedy-uzywac-new-i-delete/


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22
AK
Popieram. Lub jeśli musi być to własnoręczne (w szkołach), zrobić własną obiektową enkapsulację
c7
Wiem, że to skrót myślowy, ale.. w C nie ma new/delete :P Dodatkowo, mogę polecić to: Cpp Guidelines
Sunnydev
jestem pewien, że jakby na necie byłyby kursy nowoczesnego cpp i "młodzi" wykładowcy na studiach, to taki problem by nie istniał, ale chyba za dużo wymagam.
c7
Jeśli chodzi o studia - to chyba jednak warto wiedzieć o co chodzi z tymi wskaźnikami - takie jest przynajmniej moje zdanie. W tych uczelnianych zadaniach chodzi bardziej o zrozumienie mechaniki, niż o wyrobienie nawyków. Fakt, że na (większości) uczelni nie uczą dobrych praktyk wynika z tego, że prowadzący nigdy nie potrzebowali(?) dobrych praktyk (albo nikt im ich nie pokazał?).
SB
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:4
0
c7 napisał(a):

Tylko oczywiście dostaje jakieś śmieciowe wyniki.

Odpowiedź znajduje się tutaj:
Jaka operacja zostanie wykonana jako pierwsza w przypadku: *arr3++ ?
(*arr3)++ czy *(arr3++)

W takim razie arr3 nie posiada początkowego adresu tablicy na stercie

Na samym początku tak, ale jeśli potem inkrementujesz pointer (czyli go przesuwasz), to nie.

Edit:

Możesz zrobić pomocniczy pointer do iteracji po tablicy i koniecznie zwracać ten, który pointuje na początek.

A faktycznie, operatory mają taki sam priorytet ale odczytujemy od prawej do lewej, czyli najpierw przechodzę o 4 bajty w prawo a potem dopiero odczytuję wartość. Ale nawet jeśli pomijam pierwsze bajty, i wychodzę poza zakres to i tak powinienem mieć część wyników prawidłowych, a cały output to śmieciowe adresy. Nie rozumiem dlaczego tak się dzieje.

TomaszLiMoon napisał(a):

Używając

Kopiuj
*arr3++

po zakończeniu pętli, zwracasz wskaźnik pokazujący adres znajdujący się za adresem ostatniego elementu tablicy.
Czytanie wartości z poza zakresu powoduje wyświetlanie różne śmieci, a zapisywanie prowadzi do UB.

Jeżeli chcesz używać tej notacji to powinieneś zapisać adres tej tablicy.

Kopiuj
int * apply_all(const int * const arr1, size_t size1, const int * const arr2, size_t size2){
    int* const arr3 = new int[size1*size2];
    int* iter = arr3;
    for (int i=0 ; i < size1; ++i) {
        for (int j=0 ; j < size2; ++j) {
            *iter++ = arr1[i] * arr2[j];
        }
    }
    return arr3;
}

Lepszym rozwiązanie jest tutaj użycie vectora, gdyż nie musisz się w tym przypadku zamartwić zwalnianiem i przydzielaniem pamięci oraz pilnowaniem zakresów tablic, co w dłuższej perspektywie uchroni cię od wielu nieprzyjemnych błędów.

Kopiuj
vector<int> multiply( const vector<int>& arr1 , const vector<int>& arr2 )
{
    vector<int> result;

    for( const auto& element1 : arr1 )
    {
        for( const auto& element2 : arr2 )
        {
            result.push_back(element1*element2);
        }
    }

    return result;
}

Wiem o możliwości użycia wektorów, ale chciałbym opanować i rozumieć dokładnie dynamiczne przydzielanie pamięci od fundamentów. Więc taka sama sprawa jak wyżej, nawet pomimo wychodzenia poza zakres i pomijana pierwszych 4 bajtów, każdy wynik to jakiś śmieciowy adres. Wydaje mi się, że środkowy przedział powinien mieć prawidłowe wartości? (chyba, że umyka mi gdzieś jakaś ważna zasada)

c7
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad rok
  • Postów:89
2
c7 napisał(a):

Edit:

Możesz zrobić pomocniczy pointer do iteracji po tablicy i koniecznie zwracać ten, który pointuje na początek.

Spróbuj zwrócić pointer na początek.

Wydaje mi się, że środkowy przedział powinien mieć prawidłowe wartości? (chyba, że umyka mi gdzieś jakaś ważna zasada)

Inkrementujesz swój wskaźnik, który zwracasz, czyli na końcu zwracasz wskaźnik za miejsce, które Cię interesuje.

SB
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:4
0
c7 napisał(a):
c7 napisał(a):

Edit:

Możesz zrobić pomocniczy pointer do iteracji po tablicy i koniecznie zwracać ten, który pointuje na początek.

Spróbuj zwrócić pointer na początek.

Wydaje mi się, że środkowy przedział powinien mieć prawidłowe wartości? (chyba, że umyka mi gdzieś jakaś ważna zasada)

Inkrementujesz swój wskaźnik, który zwracasz, czyli na końcu zwracasz wskaźnik za miejsce, które Cię interesuje.

Faktycznie, przecież modyfikuje arr3 a potem zwracam ten sam adres, który powinien być nienaruszony i wskazywać na początek tablicy w pamięci...... Dzięki wszystkim za pomoc, dopiero teraz załapałem błąd.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.