• 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ć

VPS Starter Arubacloud
+1 głos
471 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 (269,120 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,000 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,000 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,000 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ź 189 wizyt
0 głosów
1 odpowiedź 275 wizyt
0 głosów
1 odpowiedź 291 wizyt

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 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

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 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!

...