Różnica między typem tablicy a tablicą przydzieloną malloc
Dzisiaj pomagałem mojemu przyjacielowi z kodem C, i znalazłem jakieś dziwne zachowanie, którego nie mogłem wyjaśnić, dlaczego to się dzieje. Mieliśmy plik TSV z listą liczb całkowitych, z int każdej linii. Pierwsza linijka to liczba linijek na liście.
Mieliśmy również plik c z bardzo prostym "readfile". Pierwsza linia była odczytywana do n, liczby linii, następnie była inicjalizacja:
int list[n]
I wreszcie a dla pętli n Z fscanf.
Dla małe n (do ~100.000), wszystko było w porządku. Jednak odkryliśmy, że gdy n było duże (10^6), wystąpi segfault.
W końcu zmieniliśmy inicjalizację listy na
int *list = malloc(n*sizeof(int))
I wszystko, gdy dobrze, nawet z bardzo dużym n.
Czy ktoś może wyjaśnić, dlaczego tak się stało? co spowodowało segfault z int list [n], który został zatrzymany, gdy zaczynamy używać list = malloc(n*sizeof (int))?9 answers
W grze jest kilka różnych elementów.
Pierwsza jest różnicą pomiędzy zadeklarowaniem tablicy jako
int array[n];
I
int* array = malloc(n * sizeof(int));
W pierwszej wersji deklarujesz obiekt z automatycznym czasem przechowywania. Oznacza to, że tablica żyje tylko tak długo, jak długo istnieje funkcja, która ją wywołuje. W drugiej wersji otrzymujesz pamięć z dynamicznym czasem przechowywania, co oznacza, że będzie ona istnieć, dopóki nie zostanie rozdzielona z free
.
Powodem, dla którego działa tu druga wersja, jest implementacja opisująca sposób kompilacji C. Zwykle pamięć C jest podzielona na kilka regionów, w tym stos (dla wywołań funkcji i zmiennych lokalnych) i stertę (dla obiektów malloc
ed). Stos zazwyczaj ma znacznie mniejszy rozmiar niż sterta; zwykle jest to coś w rodzaju 8MB. W rezultacie, jeśli spróbujesz przydzielić ogromną tablicę z
int array[n];
Wtedy możesz przekroczyć przestrzeń dyskową stosu, powodując segfault. Z drugiej strony, sterta zwykle ma ogromny rozmiar (powiedzmy, tyle miejsca, ile jest wolne w systemie), więc malloc
duży obiekt nie spowoduje błędu poza pamięcią.
Ogólnie należy uważać na tablice o zmiennej długości w C. Mogą one łatwo przekroczyć rozmiar stosu. Preferuj malloc
, chyba że wiesz, że rozmiar jest mały lub że tak naprawdę potrzebujesz tablicy tylko na krótki okres czasu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:56:19
int list[n]
Przydziela miejsce dla n
liczb całkowitych na stosie , który jest zwykle dość mały. Używanie pamięci na stosie jest znacznie szybsze niż alternatywa, ale jest dość małe i łatwo jest przepełnić stos (tzn. przydzielić zbyt dużo pamięci), jeśli robisz rzeczy takie jak przydzielić ogromne tablice lub zrobić rekursję zbyt głęboko. Nie trzeba ręcznie dealokować pamięci przydzielonej w ten sposób, robi to kompilator, gdy tablica wychodzi poza zakres.
malloc
z drugiej strony przydziela przestrzeń w stosie , która jest zwykle bardzo duża w porównaniu do stosu. Będziesz musiał przeznaczyć dużo większą ilość pamięci na stercie, aby ją wyczerpać, ale o wiele wolniej jest przydzielać pamięć na stercie niż na stosie, i musisz ją dealokować ręcznie przez free
, Gdy skończysz z jej użyciem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:56:13
Int list [n] przechowuje dane w stosie, podczas gdy malloc przechowuje je w stercie.
Stos jest ograniczony i nie ma dużo miejsca, podczas gdy stos jest znacznie większy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:55:19
int list[n]
jest VLA, który przydziela na stosie zamiast na stosie. Nie musisz go zwalniać (zwalnia się automatycznie pod koniec wywołania funkcji) i szybko alokuje, ale przestrzeń dyskowa jest bardzo ograniczona, jak odkryłeś. Musisz przydzielić większe wartości na stercie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:55:26
Ta deklaracja przydziela pamięć na stosie
int list[n]
Malloc
Rozmiar stosu jest zwykle mniejszy niż sterty, więc jeśli przydzielisz zbyt dużo pamięci na stosie, otrzymasz przepływ stosu.
Zobacz także ta odpowiedź, aby uzyskać więcej informacji
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:50
Zakładając, że masz w swojej implementacji typową implementację to najprawdopodobniej:
int list[n]
Przydzielona lista na stosie, gdzie jako:
int *list = malloc(n*sizeof(int))
Przydzielona pamięć na Twojej stercie.
W przypadku stosu zazwyczaj istnieje limit, jak duże mogą one rosnąć(jeśli w ogóle mogą rosnąć). W przypadku sterty nadal istnieje limit, ale jest on znacznie w dużej mierze i (ogólnie) ograniczony przez przestrzeń adresową RAM+swap+, która zazwyczaj jest co najmniej kolejnością wielkość większa, jeśli nie większa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:56:35
Przy alokacji za pomocą malloc
, pamięć jest alokowana ze stosu, a nie ze stosu, który ma znacznie bardziej ograniczony rozmiar.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-13 21:55:54
Jeśli korzystasz z Linuksa, możesz ustawić ulimit - s na większą wartość i może to działać również w przypadku alokacji stosu. Kiedy przydzielasz pamięć na stosie, pamięć ta pozostaje do końca wykonywania funkcji. Jeśli przydzielisz pamięć na stercie (używając malloc), możesz zwolnić pamięć w dowolnym momencie(nawet przed zakończeniem wykonywania funkcji).
Ogólnie, heap powinien być używany do dużych alokacji pamięci.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-14 04:41:09
int array[n];
Jest to przykład statycznie przydzielonej tablicy i podczas kompilacji rozmiar tablicy będzie znany. A tablica zostanie przydzielona na stosie.
int *array(malloc(sizeof(int)*n);
Jest to przykład dynamicznie przydzielanej tablicy i rozmiar tablicy będzie znany użytkownikowi w czasie wykonywania. A tablica zostanie przydzielona na stercie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-31 14:22:19