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))?
Author: bolov, 2012-05-14

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 malloced). 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 mallocduż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.

Mam nadzieję, że to pomoże!
 129
Author: templatetypedef,
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.

 8
Author: Seth Carnegie,
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.

 2
Author: Asier Gutierrez,
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.

 1
Author: Puppy,
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

 1
Author: thumbmunkeys,
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.

 1
Author: Flexo,
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.

 0
Author: Tibor,
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.

 0
Author: Manik Sidana,
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.

 0
Author: cammando,
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