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

Wskaźniki - program przestał działać

42 Warsaw Coding Academy
+1 głos
697 wizyt
pytanie zadane 5 października 2021 w C i C++ przez Hpst Nowicjusz (130 p.)

Witam. Uczę się programowania z kursu C++ pana Zelenta. Napisałem program identyczny do tego przedstawionego w kursie, ale gdy go odpalę wyskakuje okienko "program x.exe przestał działać". Gdy je zamknę program zaczyna znów działać, ale zamiast normalnego "process returned 0" pokazuje "process returned -1073740940 (0xC0000374)

Program wygląda tak:

#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;
}


 

1 odpowiedź

–1 głos
odpowiedź 5 października 2021 przez Wiciorny Ekspert (280,970 p.)

"process returned -1073740940 (0xC0000374)
 

Problemy z pamięcią i pamięcią

Programy komputerowe uzyskują dostęp do pamięci systemowej w celu przetwarzania. Ludzie czerpią z własnej, przechowywanej pamięci, aby wykonywać codzienne zadania. Gdy program komputerowy próbuje uzyskać dostęp do nieistniejącej pamięci lub w inny sposób uzyskać dostęp do pamięci w niewłaściwy sposób (omówimy to później), ulegnie awarii.

Rozważ następujący scenariusz.

Naruszenie dostępu do pamięci , zwany również wina segmentacja (lub segfault), występuje wtedy, gdy próbuje uzyskać dostęp do programu lokalizację pamięci, która nie istnieje, lub jest w inny sposób niedostępne. Nazywamy to próbą uzyskania dostępu do nielegalnej lokalizacji w pamięci. Ta pamięć albo nie istnieje, albo nie wolno nam jej dotykać.

Aby lepiej zrozumieć to pojęcie, spójrzmy na samą pamięć. Pamięć programu składa się z segmentów (stąd błąd segmentacji słów kluczowych) lub bloków. Spójrzmy na te segmenty.

program został zamknięty, ale może jego proces trwa... w tym wypadku otwierasz ponownie ten sam program i to jest komunikat błędu odwołania się do tego samego zasobu. 
Zwrot 0; jest punktem wyjścia z programu
Więc generalnie w code-block ten mechanizm może to powodować  

3
komentarz 5 października 2021 przez tkz Nałogowiec (42,020 p.)
Nie, nie o to chodzi. Przesuwa "wskaźnika" do tablicy, gdzie jest to zabronione. Temat był poruszany kilka dni temu. https://forum.pasja-informatyki.pl/551616/10-odcinek-kursu-c
komentarz 6 października 2021 przez draghan VIP (106,230 p.)
Nit: inkrementacja wskaźnika nie jest zabroniona. Nielegalne jest przekazanie do `delete[]` adresu, który nie został zwrócony przez `new[]`.
komentarz 6 października 2021 przez tkz Nałogowiec (42,020 p.)

Nie. Zabronione jest  inkrementowanie zmiennej tablicowej, która jest niejako wskaźnikiem, ale też nie. W tym przypadku ten "wskaźnik" nie może być inkrementowany. 

int *tablica;
tablica=new int [ile];

 

komentarz 6 października 2021 przez draghan VIP (106,230 p.)

Nie ma tutaj żadnej "zmiennej która jest niejako wskaźnikiem ale też nie". Zmienna `tablica` jest typu `int*` - jest wskaźnikiem. Wskaźniki można poddawać operacjom arytmetycznym. W wyrażeniu `tablica=new int [ile];` nie dzieje się pod tym względem nic magicznego - fundamentem C++ jest to, że nie da się w nim zmienić zadeklarowanego typu zmiennej - `tablica` pozostaje wciąż wskaźnikiem. W tym wyrażeniu nawet nie dokonuje się degradacja z tablicy do wskaźnika (array-to-pointer decay), bo operator new[] zwraca po prostu wskaźnik:

Return value

1-4) non-null pointer to suitably aligned memory of size at least size

5-8) non-null pointer to suitably aligned memory of size at least size, or null pointer on allocation failure

9-10) ptr

11-22) same as (1-4) if the function does not return on allocation failure, otherwise same as (5-8)

[source]

komentarz 7 października 2021 przez tkz Nałogowiec (42,020 p.)

W wyrażeniu `tablica=new int [ile];` nie dzieje się pod tym względem nic magicznego - fundamentem C++ jest to, że nie da się w nim zmienić zadeklarowanego typu zmiennej - `tablica` pozostaje wciąż wskaźnikiem. 

Oczywiście, że nie. new działa inaczej i na indeksie "ujemny" deklaruje rozmiar dla delete. Nie możesz go przesuwać i już. 

int main()
{
    const auto size{100'000};
    int* tab = new int[size];
    for(auto i{0}; i < size; ++i)
    {
        tab++;
    }
    delete[]tab;
}

Spróbuj to skompilować. Dodatkowo, rozróżnij inkrementacje od wyrażeń arytmetycznych, bo o nich pisałem. 

Abstrahując. że myślenie o tym, co zwraca new jako zwykły wskaźnik jest niejako błędne. I nie wiem z jakiego powodu jest to powtarzane jak mantra. Należy odróżnić typ tablicowy, od wskaźnika. 

komentarz 7 października 2021 przez draghan VIP (106,230 p.)

new działa inaczej i na indeksie "ujemny" deklaruje rozmiar dla delete.

?

Nie możesz go przesuwać i już. 

Oczywiście że mogę. `tab` w tym kodzie jest zwykłym, ordynarnym wskaźnikiem, więc mogę z nim robić wszystko to, co jest legalne dla zwykłych, ordynarnych wskaźników. W tym go inkrementować (co jest efektywnie dodaniem doń 1, co zaś z kolei zalicza się do wskaźnikowych operacji arytmetycznych).

Kod, który mi każesz skompilować (btw. [niestety] kompiluje się bez problemu na GCC, Clang i MSVC) ma UB w linii 9, który dotyczy podania do `delete[]` nieprawidłowego adresu:

2) Called by delete[]-expressions to deallocate storage previously allocated for an array of objects. The behavior of the standard library implementation of this function is undefined unless ptr is a null pointer or is a pointer previously obtained from the standard library implementation of operator new[](size_t) or operator new[](size_t, std::nothrow_t).

[source]

Dodałem do niego kilka komentarzy, które ilustrują cały bałagan oraz przykładowy fix w linii 11.

int main()
{
    const auto size{ 100'000 };
    int* tab = new int[size];
    // delete[] tab;  // here would be OK, as `tab` points to address returned by `new[]`
    for (auto i{ 0 }; i < size; ++i)
    {
        tab++; // totally fine, it's just moving the pointer, BUT now you cannot pass this pointer to `delete[]`
    }
    // delete[]tab; // it's UB here, as tab doesn't point to address returned by new[] (it's incremented `size` times)
    tab -= size; // moving the pointer back to where we came from, to properly free the memory up
    delete[]tab; // now it's ok
}

Abstrahując. że myślenie o tym, co zwraca new jako zwykły wskaźnik jest niejako błędne. I nie wiem z jakiego powodu jest to powtarzane jak mantra.

Ale.. skoro w dokumentacji piszą że jest zwracany wskaźnik, to jak inaczej mam o tym myśleć? :)

Należy odróżnić typ tablicowy, od wskaźnika. 

Oczywiście że należy rozróżnić:

int tab[3]; // to jest zmienna typu tablicowego: `int[3]`
int *p; // to jest wskaźnik
p = new int[3]; // `p` to jest wciąż wskaźnik, pokazujący na zaalokowany ciągły obszar pamięci który pomieści 3 wartości typu `int`

Jeśli mi wciąż z jakiegoś powodu nie wierzysz, to spójrz co kompilatory mają w tej kwestii do powiedzenia:

#include <iostream>
#include <typeinfo>

int main()
{
    int i;
    int tab[3];
    int *p = new int[3];
    std::cout << "i is of type " << typeid(i).name() << '\n';
    std::cout << "tab is of type " << typeid(tab).name() << '\n';
    std::cout << "p is of type " << typeid(p).name() << '\n';
}

Wyniki:

i is of type int
tab is of type int [3]
p is of type int *
i is of type i
tab is of type A3_i
p is of type Pi

Zupełnie nie wiem skąd Ci się wzięło to, że nie można inkrementować wskaźnika, do którego przypisany jest adres otrzymany w wyniku wyrażenia new. Mógłbyś mi pokazać w dokumentacji/standardzie(/gdziekolwiek w internecie?) odpowiedni zapis?

Podobne pytania

0 głosów
1 odpowiedź 259 wizyt
0 głosów
1 odpowiedź 427 wizyt
0 głosów
1 odpowiedź 586 wizyt

93,389 zapytań

142,386 odpowiedzi

322,549 komentarzy

62,750 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

VMware Cloud PRO - przenieś swoją infrastrukturę IT do chmury
...