• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Odc. 10 C++ MZ - Dynamiczne alokowanie pamięci, błąd przy wyświetlaniu adresów pamięci - dlaczego!?

Object Storage Arubacloud
0 głosów
535 wizyt
pytanie zadane 13 lutego 2016 w C i C++ przez easytodo Mądrala (5,380 p.)
edycja 13 lutego 2016 przez easytodo

Przerabiam odcinek 10 kursu C++ Pana Mirosława i program (identyczny z programem na odcinku) wyrzuca błąd dla niektórych wartości 'ile'. (Zostaje wyświetlone 'ile' adresów, ale następnie zamiast normalnego 'return 0' konsola przestaje odpowiadać).

KOD:

#include <iostream>

using namespace std;

int ile;

int main()
{
    cout << "Ile liczb w tablicy: ";
    cin>>ile;
    int *tablica;
    tablica = new int [ile];

    for (int i=0;i<ile;i++)
    {
        cout<<(int)tablica<<endl;
        tablica++;
    }

    delete [] tablica;

    return 0;
}

"Program przestaje działać" dla ile o wartościach: 4, 10, 12, 14, 16, 18, 20. Powyżej 20 już nie sprawdzałem. Strasznie ciekawi mnie, dlaczego tak się dzieje, szczególnie, że na odcinku zostało wyświetlone 10 adresów pamięci bez problemu.

@EDIT: Pobawiłem się trochę i po wpisaniu jakichś wartości do szufladek tablicy adresy wyświetlają się poprawnie. 

for (int i=0;i<ile;i++)
        {
            tablica[i]=0;
            tablica[i]+=i;
        }

To dopisałem przed wyświetlaniem adresów i działa. Ale nadal jestem ciekaw czemu bez inicjacji tablicy wyrzucany jest błąd, jakiś pomysł? :)

1 odpowiedź

+2 głosów
odpowiedź 13 lutego 2016 przez draghan VIP (106,230 p.)
wybrane 14 lutego 2016 przez easytodo
 
Najlepsza

Masz tutaj jeden poważny problem i dwa mniejsze.

Zacznijmy od mniejszych - takich, których eliminowanie wypracowuje dobre nawyki.

1. Nie używamy zmiennych globalnych. W tym trywialnym programie to jeszcze nie szkodzi, ale jak miałbyś już o kilka funkcji więcej, mógłby się zrobić kłopot.

2. Nie używamy rzutowania w stylu C, czyli tego z okrągłymi nawiasami wokół typu. C++ posiada własne operatory rzutowania i to ich należy używać. Więcej tutaj. W tym przypadku odpowiednim operatorem będzie reinterpret_cast<int>().

No i teraz gwóźdź (do trumny) dzisiejszego programu. :)

W pętli przesuwasz wskaźnik o ile w przód. A potem próbujesz zwolnić pamięć, która nie została Tobie przydzielona. System przydzielił Tobie pamięć i dał Ci adres do początku tej pamięci. Żeby ją zwolnić, musisz podać do operatora delete[] dokładnie ten sam adres, który otrzymałeś od systemu. Czyli należy napisać:

    tablica -= ile;
    delete [] tablica;

 

komentarz 13 lutego 2016 przez easytodo Mądrala (5,380 p.)

Wyłączyłem ten projekt i napisałem kilka innych, teraz jak do niego wróciłem to działa... Nawet bez dopisania Twojego kodu. Teraz to mam papkę zamiast mózgu. 

Mam pytanie - rozumiem, że przy zwalnianiu miejsca funkcją delete musimy wskazać pierwszą szufladkę tablicy (tutaj 'tablica'). Czyli jeżeli nie dodam:

tablica -= ile;

to zwolnię pamięć tylko z ostatniej szufladki?

Do zmiennych globalnych mam wstręt, tutaj zmieniłem zmienną lokalną na globalną w akcie desperacji (a nuż by to coś dało :) ).

Z rzutowania korzystałem pierwszy raz, jednak dziękuję za przydatnego tipa w tej sprawie :)

komentarz 14 lutego 2016 przez draghan VIP (106,230 p.)

teraz jak do niego wróciłem to działa... Nawet bez dopisania Twojego kodu. Teraz to mam papkę zamiast mózgu. 

Kwestia szczęścia. Tak napisany program jest niepoprawny - raz zadziała, raz nie.

rozumiem, że przy zwalnianiu miejsca funkcją delete musimy wskazać pierwszą szufladkę tablicy (tutaj 'tablica').

Dokładnie tak.

Czyli jeżeli nie dodam: (...) to zwolnię pamięć tylko z ostatniej szufladki?

Nie.

Po pierwsze, w pętli przestawiasz swój wskaźnik już jeden element poza tablicę (w ostatniej iteracji).

A po drugie, program próbuje zwolnić tyle bajtów, ile rezerwował.

Czyli od miejsca, które wskazuje dany wskaźnik. Jeśli wskaźnik nie pokazuje na zerowy element tablicy, to program próbuje zwolnić coś, czego nie powinien... W efekcie nie zwalnia nic i rzuca wyjątkiem czy innym śmiercionośnym bumerangiem.

W dokumentacji cppreference piszą, że użycie free() (które jest wewnętrznie wywoływane przez operator delete[], przynajmniej w implementacji GCC) dla niepoprawnego wskaźnika powoduje Undefined Behaviour - czyli coś, czego w żadnym razie nie chcemy.

komentarz 14 lutego 2016 przez easytodo Mądrala (5,380 p.)
No i wszystko jasne! Teraz to ma sens. Dzięki wielkie za wyjaśnienie :)

Podobne pytania

0 głosów
1 odpowiedź 1,684 wizyt
pytanie zadane 4 sierpnia 2017 w C i C++ przez Darven Użytkownik (860 p.)
0 głosów
2 odpowiedzi 165 wizyt
0 głosów
1 odpowiedź 1,546 wizyt

92,584 zapytań

141,433 odpowiedzi

319,668 komentarzy

61,966 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...