Dostęp do nieistniejących miejsc dynamicznych tablic dwuwymiarowych

0

Witam,
Czy mógłby mi ktoś wytłumaczyć czemu takie coś działa:

int *tab= new int[5] ;
tab[6]=1;
cout << tab[6];
 

takie coś także:

int **tab= new int*[5] ;

for (int i=0; i<5;++i)
tab[i]=new int[5];

tab[4][10]=1;
cout << tab[4][10];
 

ale takie coś nie:

int **tab= new int*[5] ;

for (int i=0; i<5;++i)
tab[i]=new int[5];

tab[10][10]=1;
cout << tab[10][10];
 
3

Próbujesz odwoływać się do niezaalokowanych elementów. Masz miejsce na 5 wskaźników, odwołujesz się do szóstego, czasami dziesiątego.

P.s. ten pierwszy przykład też nie powinien działać.

Edit:
P.s. 2. Nic z powyższego nie powinno działać, bo wszędzie przekraczasz zakresy zaalokowanych tablic.

2

To działa, bo akurat nie najechałeś nikomu na odcisk. Jakby za &int[4] była pamięć zaalokowana przez inny proces, to dostałbyś segfaulta od systemu i już by nie działało. To jest fart, że działa.

A ostatni przykład się sypie, bo tab[5], które nie jest Twoje najpewniej ma wartość 0 i robisz dereferencje na NULLu i papa.

1

Jakby za &int[4] była pamięć zaalokowana przez inny proces, to dostałbyś segfaulta od systemu i już by nie działało.

Mało prawdopodobne; bardziej prawdopodobne jest, że będzie tam inna nasza zmienna, którą po cichu uszkodzimy.

Są też możliwe bardziej zaskakujące efekty wynikające z UB (undefined behavior) po stronie kompilatora.

0

OP nie pytał jakie mogą być skutki zapisu poza zaalokowaną pamięć, tylko dlaczego zapis poza nią nie wywalał programu. Tak, jest spora szansa, że tam będzie nasza inna pamięć, jest szansa, ze będzie próżnia, jest też szansa, że będzie pamięć innego procesu. I tylko ostatni przypadek wysadzi program od razu (jak system wspiera oddzielne przestrzenie adresowe dla procesów, co przy np mikrokontrolerach nie zawsze jest), reszta spowoduje ciężkie do znalezienia bugi.

Możesz rozwinąć jakie zaskakujące UB zrobi kompilator gdy każesz mu pisać poza zaalokowaną pamięcią? Bo jak dla mnie to, to co zrobi sam kompilator jest jak najbardziej zdefiniowane - po prostu skompilowany program będzie próbować zapisywać pod żądany adres. Chyba, że jest jakaś magia o której nie wiem?

1
mlyszczek napisał(a)

Możesz rozwinąć jakie zaskakujące UB zrobi kompilator gdy każesz mu pisać poza zaalokowaną pamięcią? Bo jak dla mnie to, to co zrobi sam kompilator jest jak najbardziej zdefiniowane - po prostu skompilowany program będzie próbować zapisywać pod żądany adres.

W zasadzie UB oznacza "brak gwarancji czegokolwiek". Faktycznie, może się tam pojawić mov/store do adresu poza, a równie dobrze któraś z optymalizacji może wyrzucić taki dostęp z kodu. Problem polega na tym, że jak to zwykle z UB bywa - nie wiesz, na czym stoisz.

1

To jest po prostu pisanie po pamięci zaalokowanej gdzie indziej (lub nie zaalokowanej).
Nie wiem czy tu kompilator coś w ogóle może zmajstrować - zwłaszcza że może zajść sytuacja że to jest akurat dobra pamięć tylko zalokowana inaczej (gdzie indziej).

Oczywiście ten kod w OP jest błędny i okazyjnie może się wywalić, ale nie musi. Taki urok C/C++.

Pomocne może być: https://www.ichec.ie/support/tutorials/valgrind

BTW, o ile wiem, to taki kod nie ma prawa pisać po pamięci innego procesu, chyba że chodzi o system bez pamięci wirtualnej (np. MorphOS).

2
mlyszczek napisał(a)

Możesz rozwinąć jakie zaskakujące UB zrobi kompilator gdy każesz mu pisać poza zaalokowaną pamięcią? Bo jak dla mnie to, to co zrobi sam kompilator jest jak najbardziej zdefiniowane - po prostu skompilowany program będzie próbować zapisywać pod żądany adres. Chyba, że jest jakaś magia o której nie wiem?

vpiotr napisał(a)

Nie wiem czy tu kompilator coś w ogóle może zmajstrować - zwłaszcza że może zajść sytuacja że to jest akurat dobra pamięć tylko zalokowana inaczej (gdzie indziej).

Niestety, to co kompilator zrobi jest właśnie niezdefiniowane. Możliwa jest całkiem zaawansowana magja.

https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633

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