Dynamiczna alokacja pamięci - tak na prawdę po co?

0

Witam

Mam takie pytanie. Piszę sobie program w C i doszedłem do działu zajmującego się dynamiczną alokacją pamięci. Zawsze mi się wydawało, że służy ona do tego aby przykładowo powołać do życia tablicę, która nie jest znana w momencie pisania programu. Problem w tym, że napisałem sobie przed chwilą program następujący:

#include <stdio.h>	
 
int main() 
{
	printf("Podaj liczbe: ");
        int liczba;
        scanf("%d",&liczba);   // podaję 10
	printf("Liczba c=%d\n",liczba);
	
	int tablica[liczba];
	tablica[2] = 5;   // załóżmy że indeks 2 istnieje
	
	printf("%d",tablica[2]);
	
	system("PAUSE");
	return 0;
}

i o dziwo działa. Myślałem, że wobec powyższego kompilator zaprotestuje... a tak się nie stało. Czyżby jednak dało się powoływać do życia tablice, których rozmiar zostanie wyliczony w trakcie działania programu? Jeśli tak, to nie bardzo widzę sens stosowania malloc'ów i calloc'ów gdyż jedynie mogłyby się przydać wtedy gdy tablice na których operujemy naprawdę co chwila zmieniają rozmiar i chcemy super zoptymalizować nasz program.

1

Tak stworzona tablica leży na stosie. Stos jest mały (kilka(naście) MB). Dużej tablicy tak nie zrobisz ;)
Co więcej, nie możesz tak stworzyc tablicy wewnątrz funkcji i przekazać jej poza funkcje, bo dane ze stosu automatycznie zostaną zwolnione.
Poza tym dynamiczna alokacja przyda sie bardziej w dynamicznych strukturach danych -> listy, drzewa etc

1

To nie jest dopuszczalne z punktu widzenia standardu, ale kompilator GNU pozwala na tworzenie tablic na stosie nie znając wczęsniejszej wartości. Jednakże tablice te są jak już wspomniałem definiowane na stosie, to znaczy, że po wyjściu poza zakres ( poza znak '}' ) przestają istnieć. Natomiast tablice definiowane na stercie ( za pomocą malloc ) żyją do momentu aż sami nie zwolnimy pamięci ( free ). Jest to też zmora, ponieważ jeżeli stracimy jakikolwiek wskaźnik na ten obszar, to nie odzyskamy go, co więcej dojdzie do wycieku pamięci i pamięć zwolni się dopiero po ponownym uruchomieniu komputera.

1

Tak właśnie myślałem, że coś nie tak jest z kompilatorem (a raczej trzymania się przez niego standardów) i tak na prawdę nie można tak robić.

Bardzo dziękuję za rzeczowy wytłumaczenie sprawy.

0

Możesz też skompilować kod z flagą -pedantic

#include<stdio.h>

int main(void) {
int a;

printf("Podaj liczbe:\n");
scanf("%d", &a);

int tablica[a];

return 0;
}
tab.c: In function 'main':
tab.c:9:1: warning: ISO C90 forbids variable length array 'tablica' [-Wvla]
tab.c:9:1: warning: ISO C90 forbids mixed declarations and code [-pedantic]

Przy czym to tylko ostrzeżenie a nie błąd (według GNU GCC). Jak wrócę do domu, to zobaczę bo może w standardzie dla C99 o tym coś napisali to bym wkleił (z draftu rzecz jasna).

0

Przede wszystkim alokacja pamięci na stosie jest w cholerę szybsza niż alokacja na stercie. Alokacja na stosie to tylko dodanie czy odjęcie jakiejś liczby od wskaźnika stosu, natomiast alokacja na stercie to odwołanie do menedżera pamięci i być może nawet odwołanie do jądra.

Jednakże tablice te są jak już wspomniałem definiowane na stosie, to znaczy, że po wyjściu poza zakres ( poza znak '}' ) przestają istnieć. Natomiast tablice definiowane na stercie ( za pomocą malloc ) żyją do momentu aż sami nie zwolnimy pamięci ( free ).

Tablice, czy to zwykłe czy VLA, nie są wyjątkowe i zwykłe struktury/ obiekty czy prymitywy podlegają tym samym regułom. Jeśli prześlę gdzieś wskaźnik do lokalnego obiektu to po wyjściu z funkcji ten wskaźnik będzie wskazywał na zwolnioną/ nadpisaną pamięć. Nie rozumiem czemu te reguły są opisywane jako wada VLA, skoro te reguły dotyczą wszystkich obiektów na stosie.

1

To nie jest dopuszczalne z punktu widzenia standardu, ale kompilator GNU pozwala na tworzenie tablic na stosie nie znając wczęsniejszej wartości.

Dużo razy ktoś z tym jeszcze wyskoczy? Fajnie że ktoś wie że w C++ tak się nie powinno robić, ale w C99 takie coś jest dozwolone, a mówienie czegoś przeciwnego tylko wprowadza zamieszanie i dezinformację.
Zacytuję:

Tak właśnie myślałem, że coś nie tak jest z kompilatorem (a raczej trzymania się przez niego standardów) i tak na prawdę nie można tak robić.
Świetnie, jedna osoba wprowadzona w błąd.

Standard C99, 6.7.5.2 Array declarators, punkt 4:

c99 napisał(a)

If the size is not present, the array type is an incomplete type. If the size is * instead of
being an expression, the array type is a ** variable length array** type of unspecified size,
which can only be used in declarations with function prototype scope;124) such arrays are
nonetheless complete types. if the size is an integer constant and the element type has a known constant
size, the array type is not a variable length array type;
otherwise, the array type is a variable length array type.

0

Dużo razy ktoś z tym jeszcze wyskoczy?

Zamieszanie bierze się stąd, że mało kto śledzi standardy i nadąża co jest w którym, czego nie ma, a co jest rozszerzeniem danego kompilatora.

Moje podejście jest takie: niezależnie od języka, jeśli na danej platformie i pod danym kompilatorem to działa, to można używać (o ile jest potrzeba, nie na siłę).
Jeśli kompilator nie łyka – nie używać (no trudno, żeby).

1
MJay napisał(a)

co więcej dojdzie do wycieku pamięci i pamięć zwolni się dopiero po ponownym uruchomieniu komputera
kto ci dziecko takich głupot naopowiadał? Pamięć ta zostanie zwolniona przez każdy współczesny system operacyjny.

0

@programmers poczytaj cos zanim odezwiesz sie glupio, system operacyjny zwolni pamiec pod warunkiem, ze jakikolwiek wskaznik pokazuje na dane miejsce w pamieci, w przeciwnym razie zostanie zarezerwowana pamiec i NIE zostanie zwolniona. Fakt, ze wczesniej nawiazywalem do standardu C++. Nie wiedzialem, ze standard C99 na to pozwala, myslalem, ze tylko kompilatory.

0

Pojechali po mnie informatycy, a ja wiem co czytam w internecie.
Otóż tutaj ( http://directxtutorial.com/Tutorial10/B-Direct3DBasics/dx10B1.aspx#still ) w drugiej lekcji już jest napisane przy omawianiu:
pBackBuffer->Release( ):
"it will just keep on running in the background of the computer until your next reboot",
oraz przy funkcji CleanD3D( ):
"objects will still exist in the background".

Prawdopodobnie teraz będą szły odpowiedzi, że jeden artykuł jakiegoś mało znanego autora nie jest właściwym dowodem, w takim układzie druga strona to: http://rastertek.com/dx10tut02.html
Także już w drugiej lekcji jest o tym wspomniane przy pisaniu pustego destruktora klasy SystemClass:
"Certain windows functions like ExitThread() are known for not calling your class destructors resulting in memory leaks. You can of course call safer versions of these functions now but I'm just being careful when programming on windows."

Minusujcie mnie dalej, wali mnie to. Nie znam standardu C'99 i byćmoże jest możliwość tworzenia tablic w ten sposób opisany wyżej - nie interesuje mnie tamten standard. Ale wyciek pamięci nie zostanie usunięty przez system.

Pozdrawiam 4p.

0

Minusujcie mnie dalej, wali mnie to. Nie znam standardu C'99 i byćmoże jest możliwość tworzenia tablic w ten sposób opisany wyżej - nie interesuje mnie tamten standard. Ale wyciek pamięci nie zostanie usunięty przez system.

Nikt Cię nie minusuje dlatego że Cię nie lubi tylko dlatego że po prostu głupoty piszesz. I upierasz się jak dziecko (Ale wyciek pamięci nie zostanie usunięty przez system).

Teraz rzucasz parę linków z kursu DirectX-a:...

Także już w drugiej lekcji jest o tym wspomniane przy pisaniu pustego destruktora klasy SystemClass:
"Certain windows functions like ExitThread() are known for not calling your class destructors resulting in memory leaks. You can of course call safer versions of these functions now but I'm just being careful when programming on windows."

Zgadza się, ExitThread nie wywołuje twoich destruktorów. Ale tak czy inaczej po zakończeniu działania procesu system wszystko zwolni...

Otóż tutaj ( http://directxtutorial.com/Tut[...]rect3DBasics/dx10B1.aspx#still ) w drugiej lekcji już jest napisane przy omawianiu:
pBackBuffer->Release( ):
"it will just keep on running in the background of the computer until your next reboot",
oraz przy funkcji CleanD3D( ):

Świadomie manipulujesz czy naprawdę nie czytasz dokładnie?
Troszkę większy cytat:

Basically, if you create a COM object, but never close it, it will just keep on running in the background of the computer until your next reboot, even after the program itself closes.

Jak się zachowuje COM po wyłączeniu aplikacji nie wiem (ściśle to obiekty COM liczą referencje do siebie - pytanie czy system operacyjny dekrementuje licznik automatycznie), ale wypada go odróżniać od zaalokowanej pamięci.
Itd, itd...

0
MJay napisał(a)

Pojechali po mnie informatycy, a ja wiem co czytam w internecie.

Czytasz g**no, w tym tkwi problem. Znasz pojęcie "wiarygodne źródła"?

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686722(v=vs.85).aspx:

Terminating a process has the following results:

  • Any remaining threads in the process are marked for termination.
  • Any resources allocated by the process are freed.
  • All kernel objects are closed.
  • The process code is removed from memory.
  • The process exit code is set.
  • The process object is signaled.

Dokumentacja COM i DirectX nie wspomina o tego typu wyjątkach. Na forach technetu są jedynie informacje o pojedynczych bugach związanych z memleakami podczas działania procesu. Większość pamięci używanej przez nowoczesne gry to to właśnie obiekty DX, w takim razie każde wyłożenie się takiego np. Crysisa powinno skutkować permanentną stratą 1-2GB. Czy to ma miejsce w rzeczywistości?

0

To byłoby głupie, bo biorąc pod uwagę liczbę błędów we wszystkich aplikacjach, to Windows by się od tego wykrzaczał parę razy dziennie. Może w czasach Win9x tak było, ale dzisiejsze windowsy są stabilne.

0

@MJay, plączesz 3 wątki w jeden:

  • alokacja pamięci na stercie
  • alokacja obiektów COM
  • alokacja w podsystemie DirectX

To są trzy różne rzeczy.
COM jestem w stanie sobie wyobrazić że się nie zwalnia bo to komunikacja międzyprocesowa + serwer i jeśli jakiś proces po sobie nie posprząta to możliwe że system radośnie dalej udostępnia obiekt zamiast go zwolnić.

Direct X to z kolei pamięć powiązana z kartą graficzną z której pamięcią mogą się dziać różne rzeczy - ALE NIE O TYM BYŁ WĄTEK.
Jak program zapisze plik tekstowy do C:\temp to system też go automatycznie nie zwolni (o ile go zamknął)...

0

To ja może wrócę do tematu czyli po cóż nam ta dynamiczna alokacja.

Ograniczony rozmiar stosu to jedno. Ale nikt nie wspomniał o ważniejszej rzeczy - chodzi o czas życia obiektu.
Obiekt tworzony na stosie będzie niszczony po wyjściu z funkcji, która go utworzyła. Jeśli chcemy, aby obiekt "żył" dłużej musi być utworzony na stercie. Wtedy sami decydujemy kiedy go zniszczyć.

Natomiast jeśli chodzi o nieznaną długość tablicy - to nie dotyka to tematu. Taką tablicę zawsze się da utworzyć na stosie (alloca) lub na stercie (malloc).

0
MJay napisał(a)

Minusujcie mnie dalej, wali mnie to
Gdyby ciebie to waliło nie zmarnowałbyś tyle czasu na szukanie (błędnej) odpowiedzi.

MJay napisał(a)

wyciek pamięci nie zostanie usunięty przez system.
Rzeczywiście AmigaOS może sobie nie sprzątać ale współczesne systemy takie jak windows czy te z jądrem linuxowym sprzątają ale o tym dokładniej poczytasz w artykułach na temat samych systemów operacyjnych niż w dokumentacjach programistycznych. Żeby wypowiadać się na takie tematy trzeba posiadać minimum wiedzy na temat systemów operacyjnych (czyli podstawowa wiedza, która wynosi się ze szkoły średniej będąc na informatyce)

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