Zwalnianie pamięci alokowanej dynamicznie

0

Witam,
Mam problem z funkcją delete w C++. Napisałem taki program:

#include <iostream>

using namespace std;

int main(){

	int ** tablica = new int * [10];
	for(int i = 0; i <10; i++)
		tablica[i] = new int[10];

	tablica[9][9] = 1;
	cout << tablica[9][9] << endl;

	for(int i = 0; i < 10; i++)
		delete [] tablica[i];
	delete [] tablica;

	tablica[9][9] = 2;
	cout << tablica[9][9] << endl;

	return 0;
} 

Po jego kompilacji (bez błędów) i uruchomieniu dostajemy:

1
2 

Czy pamięć jest w tym przypadku zwalniana poprawnie?

0

Tak, jest zwalniana poprawnie. Kiedy system operacyjny zainteresuje się tą pamięcią to inna kwestia - jednak po delete sam zadeklarowałeś, że tej pamięci już nie chcesz. Oznacza to, że w dowolnym momencie system może zrobić z tą pamięcią coś, co spowoduje, że takie odwołanie jak u Ciebie skończy się segmentation fault.

Btw. Czasami warto po delete zrobić jeszcze wskaznik = nullptr (tudzież NULL). Wtedy na pewno niczego nie popsujesz po usunięciu.

0

Rozumiem. Czyli wskaźnik tablica wskazuje ciągle na ten sam blok pamięci i zmienna tablica[9][9] znajduje się cały czas w tym samym miejscu? Różnica polega tylko na tym, że od momentu zastosowania delete do tej pamięci mają dostęp również inne programy?

0

Poniekąd. Od momentu "usunięcia" tego obszaru system operacyjny może przydzielić tę pamięć do innego programu lub podczas innej alokacji w Twoim programie.

0

@F1fan:
Nie.

Po 1:
Granulacja stronicowania w x86 to zwykle 4 KiB - oznacza to, iż jeśli zaalokowałeś sobie coś pod adresem x, to w rzeczywistości zaalokowałeś co najmniej jedną stronę o rozmiarze 4 KiB zaczynającą się na adresie (x & -4096), a kończącą na (x | 4095). Aby uniknąć fragmentowania pamięci (ważne pod systemami 32-bitowymi, pod 64-bitowymi nie jest to takie ważne, bo ciężko tam o taką fragmentację, żeby nie dało się zaalokować dużego ciągłego bloku) system może alokować wiekszymi partiami i wtedy miałbyś od razu dostęp do większej ilości pamięci.

Po 2:
Dealokacja jest zwykle opóźniona po to, aby zwiększyć wydajność. Przebijanie się przez poziomy uprawnień w procesorze trochę trwa, tzn chodzi mi np o wywołania z kernela. Aby zmniejszyć ilość wywołań jądra, lokalny dla procesu alokator pamięci alokuje większymi partiami i dealokuje z opóźnieniem. Dzięki temu jest większa szansa na to, że przy alokowaniu będzie miał pod ręką dostępną pamięć oznaczoną jako niezaalokowaną i nie będzie musiał wykonywać kosztownych odwołań do jądra.

Po 3:
To do @Endrju napisał.

0

W takim razie czy kompilator nie powinien wyświetlić jakiegoś ostrzeżenia, że korzystamy ze zwolnionej pamięci? Używam linuksowego g++ i nie było przy kompilacji żadnej informacji o tym. A może trzeba użyć jakiegoś dodatkowego polecenia?

0

Akurat w tym przypadku mógłby, ale w ogólności nie ma jak. Dodatkowo arytmetyka wskaźników utrudnia wykrywanie czegokolwiek.

Pomocnym narzędziem jest Valgrind. Jedno z narzędzi Valgrinda - Memcheck - potrafi wykryć co nastepujące:

Use of uninitialized memory
Reading/writing memory after it has been free'd
Reading/writing off the end of malloc'd blocks
Memory leaks

Oczywiście o ile coś takiego nastąpi podczas wykonywania programu. Narzędzia do statycznej analizy są, ale chyba nie mają 100% (ani nawet bliskiej temu) skuteczności.

Jeśli chcesz poczytać o statycznej analizie to masz: http://en.wikipedia.org/wiki/Static_analysis_tool
Narzędzie do C (i chyba tylko do C, a nie do C++) to http://en.wikipedia.org/wiki/Frama-c

0

Może powinien. Wydaje mi się, że w ogólnym przypadku jest trudno coś takiego wyśledzić i kompilatory po prostu tego nie robią.

Z tego właśnie powodu w C++ masz smart pointery i pojemniki, które za Ciebie dbają o pamięć.

0

Dzięki za pomoc :)

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