Przekroczenie indeksu tablicy alokowanej dynamicznie

0

Witam, mam szybkie pytanie do Was, ponieważ dopiero uczę się podstaw tego języka.

int main()
{
    int* tab = new int[2];
    int tab_2[2];
    tab[5] = 4;
    tab_2[3] = 5;

    return 0;
}

Wcześniej bawiłem się w języku java i dziwi mnie, że program przy takiej implementacji sie nie wysypuje. Powiedzcie mi co się dzieje w środku programu w takiej sytuacji? Skoro w pierwszym przypadku jest tablica alokowana dynamicznie(tab) i nagle wpisuje cos do indeksu 5? Ona się automatycznie powiększa?
A drugie pytanie to co z normalna tablicą(tab_2), która też kompilator przepuścił ?

Pozdrawiam

3

Ona się automatycznie powiększa?

Nie, nadpisujesz kawałek pamięci znajdujący się poza tablicą - możesz przypadkowo np. zmienić zawartość innej zmiennej, która akurat znajdowała się pod tamtym adresem.

Jest to undefined behavior i może poskutkować crashem programu.

A drugie pytanie to co z normalna tablicą(tab_2), która też kompilator przepuścił ?

Identyczna sytuacja jak wyżej - z tym, że tutaj nadpisujesz kawałek pamięci ze stosu (a nie sterty, jak w przypadku tej alokowanej dynamicznie).

która też kompilator przepuścił ?

Na marginesie: kompilator Javy też by taki zapis przepuścił - to dopiero maszyna wirtualna rzuciłaby błąd (w trakcie działania aplikacji, nie jej kompilacji).

3

W C++ szybkość wykonywania jest najważniejsza. Dlatego domyślnie nie ma sprawdzania poprawności indeksów taj jak w Java czy C# (tam jest to domyślnie).
W standadzie jest to nazwane jako undefined behavior i generalnie chodzi o to, że w takich wypadkach kompilator może zrealizować dowolną czynność.
Kompilator może wygenerować kod sprawdzający zakres indeksów, ale nie musi. Większość ignoruje problem i zależnie od okoliczności program będzie działać dalej albo nastąpi SEG FAULT, albo coś bardziej zakręconego.

Operator new w obecnym C++ nie powinien być używany (poza drobnymi wyjątkami). W codziennej praktyce nigdy nie jest używany.
Zamiast new int[x] powinieneś używać std::vector<int>(x)

Jeśli używasz Linux gcc/clang polecam używać ASAN (address sanitizer). Opcja kompilatora na czas testów, która wykryje ci te błędy i opisze gdzie jest problem (strasznie mi tego brakuje na msvc).

3

Jeżeli chcesz aby kompilator sprawdzał zakres tablicy użyj vector i funkcji at(). Próba przypisania zmiennej z poza zakresu będzie wtedy skutkowała rzuceniem wyjątku.

vector<int> tab(5,0);
tab.at(6) = 7; // throws vector::_M_range_check
0

Okej, rozumiem, po prostu zastanawiało mnie dlaczego nie ma crashu, tylko normalnie czasami program przeleci.
Ale w takim razie mam inne pytanie. Dlaczego w takim przypadku, program się zacina, a kiedy zrobię to samo na stosie wszystko działa poprawnie. Czy ma to jakis związek z przekazywaniem tab.dynamicznej do funkcji ? :

#include <iostream>
#include <clocale>

using namespace std;
void zamien_na_duze(char* tab, int wielkosc){

    for(int i =0;i<wielkosc;i++){
        tab[i]=toupper(tab[i]);


    }

}




int main()
{
char* test = new char[10];
test = "Ala Ma kOt";
zamien_na_duze(test, 10);
    return 0;
}

2

Zamień:

int tab_2[2];

na:

std::array<int, 2> tab_2;

i

tab_2[3] = 5;

na:

tab.at(3) = 5;

i będziesz mieć sprawdzanie zakresu.

Przykład:

#include <iostream>
#include <string>
#include <array>
#include <iterator>

using namespace std;

int main()
{
    std::array<int, 2> tab;
    tab.at(5) = 4;
    
    for (const auto& e : tab) {
        std::cout << e << std::endl;
    }

    return 0;
}

Dla struktur z new użyj std::vector.

4
char* test = new char[10];
test = "Ala Ma kOt";

właśnie nadpisałeś WSKAŹNIK a nie skopiowałeś dane do tablicy! Tak się nie kopiuje stringów w C/C++ W efekcie test przechowuje pointer do danych które są stałe i w takim miejscu w binarce w którym nie wolno ich zmienić. Próba zmiany powoduje crash.

2
Niaopbi napisał(a):

Okej, rozumiem, po prostu zastanawiało mnie dlaczego nie ma crashu, tylko normalnie czasami program przeleci.
Ale w takim razie mam inne pytanie. Dlaczego w takim przypadku, program się zacina, a kiedy zrobię to samo na stosie wszystko działa poprawnie. Czy ma to jakis związek z przekazywaniem tab.dynamicznej do funkcji ? :

#include <iostream>
#include <clocale>

using namespace std;
void zamien_na_duze(char* tab, int wielkosc){

    for(int i =0;i<wielkosc;i++){
        tab[i]=toupper(tab[i]);


    }

}

int main()
{
char* test = new char[10];
test = "Ala Ma kOt";
zamien_na_duze(test, 10);
    return 0;
}

Odpowiedź krótka: zamiast się katować C pisz w C++

#include <iostream>
#include <string>
#include <locale>

std::string zamien_na_duze(const std::string& s)
{
    std::string r = s;
    for (auto &ch : r) ch = std::toupper(ch);
    return r;
}

int main()
{
    std::string test = "Ala Ma kOt";
    test  = zamien_na_duze(test);
    return 0;
}

odpowiedź długa:

  • char* test = new char[10]; alokowałeś jakąś pamięc
  • test = "Ala Ma kOt"; zmieniasz wskaźnik test by wskazywał na napis, który jest literałem (tracisz kontakt z pamięcią alokowaną wcześniej - wyciek pamięci)
  • zamien_na_duze(test, 10); teraz ponieważ test wskazuje na literał który jest stały, a kompilator umieścił ten literał w pamięci read only to porgram kończy się SEG FAULT

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