alloca()
przydziela pamięć na stosie, a nie na stercie, jak w przypadku malloc()
. Kiedy wracam z rutyny, pamięć zostaje uwolniona. Tak więc faktycznie rozwiązuje to mój problem zwalniania dynamicznie alokowanej pamięci. Zwolnienie pamięci przydzielonej przez malloc()
to poważny ból głowy i jeśli w jakiś sposób pominięty, prowadzi do różnego rodzaju problemów z pamięcią.
Dlaczego stosowanie alloca()
powyższych funkcji jest odradzane pomimo powyższych funkcji?
free
(co jest oczywiście zaletą), brakiem możliwości wstawienia go (oczywiście przydzielanie sterty jest znacznie cięższe) itp. Jedynym powodem, dla którego należy unikać,alloca
są duże rozmiary. Oznacza to, że marnowanie ton pamięci stosu nie jest dobrym pomysłem, a ponadto istnieje ryzyko przepełnienia stosu. Jeśli tak jest - rozważ użyciemalloca
/freea
alloca
jest to, że stosu nie można podzielić na fragmenty jak stertę. Może to okazać się przydatne w przypadku aplikacji działających w czasie rzeczywistym w trudnych warunkach czasu rzeczywistego, a nawet aplikacji o krytycznym znaczeniu dla bezpieczeństwa, ponieważ WCRU można następnie analizować statycznie bez uciekania się do niestandardowych pul pamięci z własnym zestawem problemów (brak lokalizacji czasowej, nieoptymalne zasoby posługiwać się).Odpowiedzi:
Odpowiedź znajduje się na
man
stronie (przynajmniej w systemie Linux ):Co nie znaczy, że nigdy nie należy go używać. Jeden z projektów OSS, nad którym pracuję, wykorzystuje go w szerokim zakresie i dopóki go nie nadużywasz (
alloca
z dużymi wartościami), jest w porządku. Gdy miniesz znak „kilkaset bajtów”, czas na użyciemalloc
i przyjaciół. Nadal możesz otrzymywać awarie alokacji, ale przynajmniej będziesz mieć pewne oznaki niepowodzenia zamiast po prostu wysadzić stos.źródło
alloca()
, nie ma takiego samego strachu przed lokalnymi tablicami lub rekurencją (w rzeczywistości wiele osób, które będą krzyczeć,alloca()
chwalą rekurencję, ponieważ „wygląda elegancko”) . Zgadzam się z radą Shauna („przydział alokacji () jest w porządku w przypadku małych przydziałów”), ale nie zgadzam się z poglądem, że ramy przydziałów () są wyjątkowo złe wśród 3 - są one równie niebezpieczne!Jednym z najbardziej pamiętnych błędów, jakie miałem, było użycie wbudowanej funkcji
alloca
. Przejawiał się jako przepełnienie stosu (ponieważ alokuje na stosie) w losowych punktach wykonania programu.W pliku nagłówkowym:
W pliku implementacyjnym:
Tak więc stało się z wbudowaną
DoSomething
funkcją kompilatora, a wszystkie alokacje stosu odbywały się wewnątrzProcess()
funkcji i tym samym wysadzały stos. W mojej obronie (i to nie ja znalazłem problem; musiałem iść i płakać do jednego ze starszych programistów, kiedy nie mogłem go naprawić), nie było to prostealloca
, to była konwersja ciągów ATL makra.Lekcja brzmi: nie używaj
alloca
w funkcjach, które Twoim zdaniem mogą być wbudowane.źródło
Stare pytanie, ale nikt nie wspomniał, że należy je zastąpić tablicami o zmiennej długości.
zamiast
Jest w standardowym C99 i istniał jako rozszerzenie kompilatora w wielu kompilatorach.
źródło
alokacja () jest bardzo przydatna, jeśli nie możesz użyć standardowej zmiennej lokalnej, ponieważ jej rozmiar musiałby zostać określony w czasie wykonywania i możesz absolutnie zagwarantować, że wskaźnik, który otrzymujesz z alokacji (), NIGDY nie zostanie użyty po powrocie tej funkcji .
Możesz być całkiem bezpieczny, jeśli tak
Prawdziwe niebezpieczeństwo wiąże się z szansą, że ktoś później naruszy te warunki. Mając to na uwadze, świetnie nadaje się do przekazywania buforów do funkcji formatujących w nich tekst :)
źródło
alloca()
przydziela pamięć, która trwa do końca funkcji. Oznacza to, że wydaje się, że nie ma prostego, wygodnego tłumaczenia na VLA zf() { char *p; if (c) { /* compute x */ p = alloca(x); } else { p = 0; } /* use p */ }
. Jeśli uważasz, że możliwe jest automatyczne tłumaczenie zastosowańalloca
VLA na zastosowania, ale wymaga więcej niż komentarza do opisania, w jaki sposób, mogę zadać to pytanie.Jak zauważono w tym poście , istnieje kilka powodów, dla których używanie
alloca
może być uważane za trudne i niebezpieczne:alloca
.alloca
różnie interpretują zamierzone zachowanie , więc przenośność nie jest gwarantowana nawet między kompilatorami, które je obsługują.źródło
alloca()
wymaga osobnych rejestrów do przechowywania wskaźnika stosu i wskaźnika ramki. Na procesorach x86> = 386 wskaźnik stosuESP
może być użyty do obu, zwalniającEBP
- chyba żealloca()
zostanie użyty.f(42, alloca(10), 43);
mógł upaść ze względu na możliwość, że wskaźnik stosu jest regulowana przezalloca()
po co najmniej jeden z argumentów jest popychany na nim.Jednym z problemów jest to, że nie jest standardem, chociaż jest szeroko obsługiwany. Gdy inne rzeczy są takie same, zawsze używałbym standardowej funkcji zamiast wspólnego rozszerzenia kompilatora.
źródło
Nie dostrzegam takiego konsensusu. Wiele silnych zalet; kilka wad:
while
lubfor
) lub w kilku zakresach pamięć gromadzi się w zależności od iteracji / zakresu i nie jest zwalniana do momentu wyjścia z funkcji: kontrastuje to z normalnymi zmiennymi zdefiniowanymi w zakresie struktury kontroli (np.for {int i = 0; i < 2; ++i) { X }
zgromadziłabyalloca
pamięć-X wymaganą w X, ale pamięć dla macierzy o stałej wielkości byłaby ponownie przetwarzana dla każdej iteracji).inline
działają wzywającealloca
, ale jeśli je wymusisz,alloca
nastąpi to w kontekście dzwoniących (tzn. stos nie zostanie zwolniony, dopóki dzwoniący nie powróci)alloca
przeszedł z nieprzenośnej funkcji / hacka do standardowego rozszerzenia, ale pewne negatywne postrzeganie może się utrzymywaćmalloc
wyraźna kontrolamalloc
zachęca do myślenia o dealokacji - jeśli jest to zarządzane za pomocą funkcji otoki (np.WonderfulObject_DestructorFree(ptr)
), wówczas funkcja zapewnia punkt dla operacji czyszczenia implementacji (takich jak zamykanie deskryptorów plików, zwalnianie wewnętrznych wskaźników lub przeprowadzanie rejestrowania) bez wyraźnych zmian w kliencie kod: czasami jest to miły model do konsekwentnego przyjmowaniaWonderfulObject* p = WonderfulObject_AllocConstructor();
- jest to możliwe, gdy „konstruktor” jest funkcją zwracającąmalloc
pamięć (ponieważ pamięć pozostaje przydzielona po zwróceniu przez funkcję wartości do zapamiętaniap
), ale nie jeśli używa „konstruktora”alloca
WonderfulObject_AllocConstructor
może to osiągnąć, ale „makra są złe”, ponieważ mogą one powodować konflikty między sobą i kodem innym niż makr oraz tworzyć niezamierzone zamiany i wynikające z tego trudne do zdiagnozowania problemyfree
ValGrind, Purify itp. mogą wykryć brakujące operacje, ale nie zawsze można wykryć brakujące wywołania „destruktora” - jedna bardzo niewielka korzyść w zakresie egzekwowania zamierzonego użycia; niektórealloca()
implementacje (takie jak GCC) używają wbudowanego makraalloca()
, więc podstawienie biblioteki diagnostycznej wykorzystania pamięci w czasie wykonywania nie jest możliwe w taki sposób, jak w przypadkumalloc
/realloc
/free
(np. ogrodzenie elektryczne)Wiem, że to pytanie jest oznaczone jako C, ale jako programista C ++ pomyślałem, że użyję C ++ do zilustrowania potencjalnej użyteczności
alloca
: poniższy kod (i tutaj w ideone ) tworzy wektor śledzący typy polimorficzne o różnej wielkości, które są przydzielane stosowo (z czas życia związany z funkcją return) zamiast przydzielonej sterty.źródło
Wszystkie pozostałe odpowiedzi są poprawne. Jeśli jednak rzecz, którą chcesz przydzielić,
alloca()
jest stosunkowo niewielka, uważam, że jest to dobra technika, która jest szybsza i wygodniejsza niż używaniemalloc()
lub w inny sposób.Innymi słowy,
alloca( 0x00ffffff )
jest niebezpieczny i może spowodować przepełnienie, dokładnie tak samo jakchar hugeArray[ 0x00ffffff ];
jest. Bądź ostrożny i rozsądny, a wszystko będzie dobrze.źródło
Wiele interesujących odpowiedzi na to „stare” pytanie, nawet niektóre stosunkowo nowe odpowiedzi, ale nie znalazłem żadnej, która wspomniałaby o tym ....
Znalazłem ten cytat w ... OK, zrobiłem go. Ale naprawdę, pomyśl o tym ....
@j_random_hacker ma rację w swoich komentarzach pod innymi odpowiedziami: Unikanie użycia
alloca()
na rzecz przewymiarowanych tablic lokalnych nie czyni twojego programu bezpieczniejszym przed przepełnieniem stosu (chyba że twój kompilator jest wystarczająco stary, aby umożliwić wstawianie funkcji, którealloca()
w takim przypadku należy zastosować uaktualnić lub, chyba że używaszalloca()
pętli wewnętrznych, w takim przypadku nie powinieneś ... nie używaćalloca()
pętli wewnętrznych).Pracowałem w środowiskach desktop / server i systemach wbudowanych. Wiele systemów wbudowanych w ogóle nie korzysta ze sterty (nawet nie łączy się z obsługą), z powodów obejmujących przekonanie, że pamięć dynamicznie przydzielana jest zła z powodu ryzyka wycieków pamięci w aplikacji, która nigdy nie zawsze restartuje się przez lata lub bardziej rozsądne uzasadnienie, że pamięć dynamiczna jest niebezpieczna, ponieważ nie można z całą pewnością stwierdzić, że aplikacja nigdy nie podzieli stosu do momentu wyczerpania fałszywej pamięci. Dlatego wbudowanym programistom pozostaje niewiele alternatyw.
alloca()
(lub VLA) może być właściwym narzędziem do pracy.Raz po raz widziałem, jak programista sprawia, że bufor przydzielany przez stos jest „wystarczająco duży, aby obsłużyć każdą możliwą sprawę”. W głęboko zagnieżdżonym drzewie wywołań wielokrotne stosowanie tego wzorca (anty -?) Prowadzi do przesadzonego użycia stosu. (Wyobraź sobie, że drzewo wywołań ma 20 poziomów głębokości, gdzie na każdym poziomie z różnych powodów funkcja ślepo przesadza bufor 1024 bajtów „po prostu dla bezpieczeństwa”, gdy generalnie użyje tylko 16 lub mniej z nich, i tylko w bardzo w rzadkich przypadkach można użyć więcej.) Alternatywą jest użycie
alloca()
lub VLA i alokuj tylko tyle miejsca na stosie, ile potrzebuje Twoja funkcja, aby uniknąć niepotrzebnego obciążania stosu. Mamy nadzieję, że gdy jedna funkcja w drzewie połączeń wymaga alokacji większej niż normalna, inne w drzewie połączeń nadal używają swoich normalnych małych alokacji, a ogólne użycie stosu aplikacji jest znacznie mniejsze niż w przypadku, gdy każda funkcja ślepo nadmiernie alokuje bufor lokalny .Ale jeśli zdecydujesz się użyć
alloca()
...Na podstawie innych odpowiedzi na tej stronie wydaje się, że VLA powinny być bezpieczne (nie łączą alokacji stosu, jeśli są wywoływane z pętli), ale jeśli używasz
alloca()
, uważaj, aby nie używać go wewnątrz pętli i upewnij się, że nie można wstawić funkcji, jeśli istnieje szansa, że zostanie wywołana w pętli innej funkcji.źródło
alloca()
jest prawda, ale to samo można powiedzieć o przeciekach pamięcimalloc()
(dlaczego więc nie użyć GC?alloca()
przy ostrożnym stosowaniu może być bardzo przydatne, aby zmniejszyć rozmiar stosu.Wszyscy już wskazywali na wielką rzecz, jaką jest potencjalne niezdefiniowane zachowanie po przepełnieniu stosu, ale powinienem wspomnieć, że środowisko Windows ma świetny mechanizm do wychwytywania tego za pomocą wyjątków strukturalnych (SEH) i ochrony stron. Ponieważ stos rośnie tylko w razie potrzeby, strony ochronne znajdują się w nieprzydzielonych obszarach. Jeśli przydzielisz do nich (poprzez przepełnienie stosu), zgłoszony zostanie wyjątek.
Możesz złapać ten wyjątek SEH i wywołać _resetstkoflw, aby zresetować stos i kontynuować na wesołej drodze. Nie jest idealny, ale jest to kolejny mechanizm pozwalający przynajmniej wiedzieć, że coś poszło nie tak, gdy coś trafi w wentylator. * nix może mieć coś podobnego, czego nie jestem świadomy.
Zalecam ograniczenie maksymalnego rozmiaru alokacji poprzez zawinięcie alokacji i śledzenie jej wewnętrznie. Jeśli naprawdę nie lubisz tego, możesz rzucić niektórymi wartownikami na górze swojej funkcji, aby śledzić wszelkie alokacje przydziałów w zakresie funkcji i sprawdzić, czy jest to poprawne względem maksymalnej dozwolonej kwoty dla twojego projektu.
Oprócz tego, że nie zezwala się na wycieki pamięci, alokacja nie powoduje fragmentacji pamięci, co jest dość ważne. Nie sądzę, że alga jest złą praktyką, jeśli używasz go inteligentnie, co zasadniczo jest prawdą we wszystkim. :-)
źródło
alloca()
może wymagać tyle miejsca, że wskaźnik stosu wyląduje na stercie. Dzięki temu atakujący, który może kontrolować rozmiaralloca()
i dane, które trafiają do tego bufora, może nadpisać stertę (co jest bardzo złe).alokacja () jest ładna i wydajna ... ale jest również głęboko zepsuta.
W większości przypadków można go zastąpić przy użyciu zmiennych lokalnych i wielkości głównej. Jeśli jest używany do dużych obiektów, umieszczenie ich na stosie jest zwykle bezpieczniejszym pomysłem.
Jeśli naprawdę potrzebujesz C, możesz użyć VLA (brak vla w C ++, szkoda). Są o wiele lepsze niż przydziała () pod względem zachowania zakresu i spójności. Widzę, że VLA jest rodzajem poprawnie wykonanej metody splita () .
Oczywiście lokalna struktura lub tablica wykorzystująca większość potrzebnej przestrzeni jest jeszcze lepsza, a jeśli nie masz takiej alokacji sterty głównej za pomocą zwykłego malloc (), prawdopodobnie jest to rozsądne. Nie widzę żadnego rozsądnego przypadku użycia, w którym naprawdę potrzebujesz albo funkcji Almla (), albo VLA.
źródło
alloca
nie tworzy nazwy, tylko zakres pamięci, który ma żywotność .alloca
lubmalloc
lubfree
bezpośrednio. Mówię takie rzeczy jak{stack|heap}_alloc_{bytes,items,struct,varstruct}
i{stack|heap}_dealloc
. Więcheap_dealloc
po prostu dzwonifree
istack_dealloc
nie ma opcji. W ten sposób alokacje stosu można łatwo obniżyć do alokacji sterty, a intencje są również bardziej jasne.Dlatego:
Nie żeby ktokolwiek napisał ten kod, ale argument wielkości, do którego przekazujesz,
alloca
prawie na pewno pochodzi z jakiegoś rodzaju danych wejściowych, które mogą złośliwie dążyć do doprowadzenia twojego programu doalloca
czegoś takiego. W końcu, jeśli rozmiar nie jest oparty na danych wejściowych lub nie ma możliwości być duży, to dlaczego nie zadeklarowałeś małego lokalnego bufora o stałej wielkości?Praktycznie cały kod używający
alloca
i / lub C99 vlas ma poważne błędy, które doprowadzą do awarii (jeśli masz szczęście) lub kompromisu uprzywilejowania (jeśli nie masz szczęścia).źródło
alloca
. Powiedziałeś, że prawie cały kod, który go używa, zawiera błąd, ale planowałem go użyć; normalnie zignorowałbym takie twierdzenie, ale przychodzę od ciebie nie zrobię. Piszę maszynę wirtualną i chciałbym przydzielić zmienne, które nie uciekają od funkcji na stosie, zamiast dynamicznie, z powodu ogromnego przyspieszenia. Czy istnieje alternatywa podejście, które ma tę samą charakterystykę wydajności? Wiem, że mogę zbliżyć się do pul pamięci, ale wciąż nie jest to tak tanie. Co byś zrobił?*0 = 9;
NIESAMOWITE !!! Wydaje mi się, że nigdy nie powinienem używać wskaźników (a przynajmniej ich wyłuskiwać). Err, czekaj. Mogę przetestować, czy jest zerowy. Hmm Chyba mogę też przetestować rozmiar pamięci, którą chcę przydzielićalloca
. Dziwny człowiek Dziwne.*0=9;
nie jest poprawny C. Jeśli chodzi o testowanie rozmiaru, który przekazujeszalloca
, przetestuj go z czym? Nie ma możliwości poznania limitu, a jeśli zamierzasz go przetestować na niewielkim ustalonym, znanym bezpiecznym rozmiarze (np. 8k), możesz równie dobrze użyć tablicy o ustalonym rozmiarze na stosie.small_constant * log(user_input)
to prawdopodobnie mamy wystarczającą ilość pamięci.Nie sądzę, żeby ktokolwiek o tym wspominał: użycie alokacji w funkcji utrudni lub wyłączy niektóre optymalizacje, które mogłyby zostać zastosowane w funkcji, ponieważ kompilator nie może znać rozmiaru ramki stosu funkcji.
Na przykład częstą optymalizacją kompilatorów C jest wyeliminowanie użycia wskaźnika ramki w funkcji, dostęp do ramki jest wykonywany względem wskaźnika stosu; więc jest jeszcze jeden rejestr do ogólnego użytku. Ale jeśli wywoływany jest alokacja w obrębie funkcji, różnica między sp i fp będzie nieznana dla części funkcji, więc tej optymalizacji nie można wykonać.
Biorąc pod uwagę rzadkość jego użycia i jego zacieniony status jako funkcji standardowej, projektanci kompilatorów całkiem prawdopodobnie wyłączają jakąkolwiek optymalizację, która mogłaby powodować problemy z przydziałem, gdyby zajęło więcej niż trochę wysiłku, aby działał z przydziałem.
AKTUALIZACJA: Ponieważ lokalne tablice o zmiennej długości zostały dodane do C, a ponieważ stanowią one bardzo podobne problemy z generowaniem kodu w kompilatorze jak przydział, widzę, że „rzadkość użycia i zacieniony status” nie ma zastosowania do mechanizmu bazowego; ale nadal podejrzewałbym, że użycie alokacji lub VLA ma tendencję do kompromitowania generowania kodu w funkcji, która ich używa. Z radością powitam wszelkie opinie projektantów kompilatorów.
źródło
Jedną z pułapek
alloca
jest to, żelongjmp
to przewija.To znaczy, jeśli zapiszesz kontekst
setjmp
, a następniealloca
trochę pamięci, a następnielongjmp
do kontekstu, możesz stracićalloca
pamięć. Wskaźnik stosu powrócił tam, gdzie był, więc pamięć nie jest już zarezerwowana; wywołanie funkcji lub wykonanie innejalloca
spowoduje zatarcie oryginałualloca
.Aby wyjaśnić, konkretnie mam na myśli sytuację, w której
longjmp
nie wraca z funkcji, w którejalloca
miała miejsce! Zamiast tego funkcja zapisuje kontekst za pomocąsetjmp
; następnie przydziela pamięćalloca
i na końcu odbywa się longjmp w tym kontekście.alloca
Pamięć tej funkcji nie jest całkowicie uwolniona; tylko cała pamięć przydzielona od czasusetjmp
. Oczywiście mówię o zaobserwowanym zachowaniu; żaden taki wymóg nie jest udokumentowany w stosunku do żadnego,alloca
który znam.Dokumentacja koncentruje się zwykle na koncepcji, że
alloca
pamięć jest powiązana z aktywacją funkcji , a nie z żadnym blokiem; że wiele wywołań poalloca
prostu pobiera więcej pamięci stosu, która jest zwalniana po zakończeniu funkcji. Skąd; pamięć jest faktycznie związana z kontekstem procedury. Kiedy kontekst jest przywracanylongjmp
, to samo dotyczyalloca
stanu poprzedniego . Jest to konsekwencja użycia samego rejestru wskaźników stosu do alokacji, a także (koniecznie) zapisania i przywrócenia wjmp_buf
.Nawiasem mówiąc, to, jeśli działa w ten sposób, zapewnia wiarygodny mechanizm celowego uwalniania pamięci, która została przydzielona
alloca
.Natknąłem się na to jako główną przyczynę błędu.
źródło
longjmp
wraca i sprawia, że program zapomina o wszystkim, co wydarzyło się na stosie: wszystkie zmienne, wywołania funkcji itp. Ialloca
jest jak tablica na stosie, więc oczekuje się, że zostaną zablokowane jak wszystko inne na stosie.man alloca
dał następujące zdanie: „Ponieważ przestrzeń przydzielona przez alokację () jest przydzielona w ramce stosu, przestrzeń ta jest automatycznie zwalniana, jeśli powrót funkcji zostanie przeskoczony przez wywołanie longjmp (3) lub siglongjmp (3).”. Jest więc udokumentowane, że pamięć przydzielona zalloca
zostaje zablokowana polongjmp
.longjmp
. Funkcja docelowa jeszcze nie powróciła. To uczyniłsetjmp
,alloca
a następnielongjmp
.longjmp
Może przewinąćalloca
plecy państwa do tego, co było wsetjmp
czasie. Oznacza to, że wskaźnik przesuwany przezalloca
ma ten sam problem co zmienna lokalna, która nie została zaznaczonavolatile
!setjmp
wtedyalloca
, a potem wtedylongjmp
,alloca
przewijanie do tyłu jest normalne. Chodzi olongjmp
to, aby wrócić do stanu, w którym został zapisanysetjmp
!Miejsce, w którym
alloca()
jest szczególnie niebezpieczne niżmalloc()
jądro - jądro typowego systemu operacyjnego ma miejsce na stos o stałej wielkości, zakodowane na stałe w jednym z jego nagłówków; nie jest tak elastyczny jak stos aplikacji. Wywołaniealloca()
z nieuzasadnionym rozmiarem może spowodować awarię jądra. Niektóre kompilatory ostrzegają przed użyciemalloca()
(a nawet VLA) pod pewnymi opcjami, które powinny być włączone podczas kompilacji kodu jądra - tutaj lepiej jest alokować pamięć na stercie, która nie jest ustalona przez zakodowany na stałe limit.źródło
alloca()
nie jest bardziej niebezpieczny niżint foo[bar];
gdziekolwiekbar
dowolna liczba całkowita.Jeśli przypadkowo napiszesz poza blok przydzielony
alloca
(na przykład z powodu przepełnienia bufora), nadpiszesz adres zwrotny swojej funkcji, ponieważ ten znajduje się „nad” na stosie, tj. Po przydzielonym bloku.Konsekwencje tego są dwojakie:
Program zawiesi się w spektakularny sposób i nie będzie możliwe określenie, dlaczego lub gdzie się zawiesił (stos najprawdopodobniej odwija się do losowego adresu z powodu nadpisanego wskaźnika ramki).
Powoduje to, że przepełnienie bufora jest wielokrotnie bardziej niebezpieczne, ponieważ złośliwy użytkownik może stworzyć specjalny ładunek, który zostałby umieszczony na stosie i dlatego może zostać wykonany.
W przeciwieństwie do tego, jeśli piszesz poza blokiem na stercie, „po prostu” dostajesz uszkodzenie stosu. Program prawdopodobnie zostanie nieoczekiwanie zakończony, ale odpowiednio odwija stos, zmniejszając w ten sposób ryzyko wykonania złośliwego kodu.
źródło
alloca
.alloca
porównaniu zmalloc
(dlatego bufor nie ma stałej wielkości na stosie).Niestety w
alloca()
prawie tak niesamowitym tcc brakuje naprawdę wspaniałego . Gcc maalloca()
.Sieje ziarno własnego zniszczenia. Z powrotem jako destruktor.
Podobnie
malloc()
jak zwraca nieprawidłowy wskaźnik w przypadku awarii, co spowoduje segfault w nowoczesnych systemach z MMU (i mam nadzieję, że zrestartują te bez).W przeciwieństwie do zmiennych automatycznych można określić rozmiar w czasie wykonywania.
Działa dobrze z rekurencją. Możesz użyć zmiennych statycznych, aby osiągnąć coś podobnego do rekurencji ogona i użyć tylko kilku innych informacji do każdej iteracji.
Jeśli pchniesz zbyt głęboko, masz pewność segfault (jeśli masz MMU).
Zauważ, że
malloc()
nie oferuje nic więcej, ponieważ zwraca NULL (który również segfault, jeśli zostanie przypisany), gdy w systemie zabraknie pamięci. Tj. Wszystko, co możesz zrobić, to zwolnienie za kaucją lub po prostu spróbować przypisać to w jakikolwiek sposób.Aby użyć
malloc()
, używam globałów i przypisuję im NULL. Jeśli wskaźnik nie ma wartości NULL, zwalniam go przed użyciemmalloc()
.Możesz również użyć
realloc()
jako ogólnego przypadku, jeśli chcesz skopiować istniejące dane. Musisz sprawdzić wskaźnik przed sprawdzeniem, czy zamierzasz skopiować lub połączyć porealloc()
.3.2.5.2 Zalety przydziału
źródło
Procesy mają tylko ograniczoną ilość dostępnego miejsca na stosie - znacznie mniej niż ilość dostępnej pamięci
malloc()
.Korzystając z niej
alloca()
, znacznie zwiększasz swoje szanse na błąd przepełnienia stosu (jeśli masz szczęście lub niewytłumaczalną awarię, jeśli nie masz).źródło
Niezbyt ładne, ale jeśli wydajność naprawdę ma znaczenie, możesz wstępnie przydzielić trochę miejsca na stosie.
Jeśli masz już maksymalny rozmiar bloku pamięci, którego potrzebujesz i chcesz zachować kontrolę przepełnienia, możesz zrobić coś takiego:
źródło
Funkcja alokacji jest świetna i wszyscy naysayers po prostu rozpowszechniają FUD.
Array i parray są DOKŁADNIE takie same, przy DOKŁADNIE takich samych ryzykach. Mówienie, że jeden jest lepszy od drugiego, jest wyborem syntaktycznym, a nie technicznym.
Jeśli chodzi o wybór zmiennych stosu względem zmiennych stosu, istnieje wiele zalet w stosunku do długo działających programów używających stosu ponad stosu dla zmiennych o czasie życia w zakresie. Unikasz fragmentacji sterty i możesz uniknąć powiększania przestrzeni procesu za pomocą nieużywanej (nieużywalnej) przestrzeni sterty. Nie musisz go sprzątać. Możesz kontrolować alokację stosu w procesie.
Dlaczego to takie złe?
źródło
W rzeczywistości nie można zagwarantować, że skorzysta ze stosu. Rzeczywiście, implementacja alokacji gcc-2.95 przydziela pamięć ze sterty za pomocą samego malloc. Również implementacja jest błędna, może prowadzić do wycieku pamięci i nieoczekiwanego zachowania, jeśli wywołasz ją w bloku z dalszym użyciem goto. Nie, żeby powiedzieć, że nigdy nie powinieneś go używać, ale czasami alokacja prowadzi do większych kosztów ogólnych, niż to, co uwalnia.
źródło
alloca
. Jak wyczyściłby pamięć, gdylongjmp
jest używany do porzucania ramek, które to zrobiłyalloca
? Kiedy ktoś miałby dzisiaj używać gcc 2.95?IMHO, alokacja jest uważana za złą praktykę, ponieważ wszyscy boją się wyczerpać limit wielkości stosu.
Nauczyłem się wiele, czytając ten wątek i kilka innych linków:
Korzystam z alokacji głównie po to, aby moje zwykłe pliki C były kompilowane na msvc i gcc bez żadnych zmian, stylu C89, bez #ifdef _MSC_VER itp.
Dziękuję Ci ! Ten wątek zmusił mnie do zarejestrowania się na tej stronie :)
źródło
Moim zdaniem, diva (), jeśli jest dostępna, powinna być używana tylko w ograniczony sposób. Bardzo podobna do użycia „goto”, całkiem spora liczba rozsądnych ludzi wykazuje dużą niechęć nie tylko do używania, ale także istnienia, funkcji diva ().
W przypadku użycia wbudowanego, gdy znany jest rozmiar stosu i można nałożyć ograniczenia poprzez konwencję i analizę wielkości alokacji, i gdzie kompilatora nie można zaktualizować do obsługi C99 +, użycie alga () jest w porządku i byłem znany z tego, że go używa.
Jeśli są dostępne, licencje VLA mogą mieć pewne zalety w stosunku do metody przydzielania (): kompilator może generować kontrole limitu stosu, które wychwycą dostęp poza granicami, gdy używany jest dostęp do stylu tablicy (nie wiem, czy którykolwiek kompilator to robi, ale może do zrobienia), a analiza kodu może ustalić, czy wyrażenia dostępu do tablicy są odpowiednio ograniczone. Należy pamiętać, że w niektórych środowiskach programowania, takich jak motoryzacja, sprzęt medyczny i awionika, analizy tej należy dokonać nawet dla tablic o stałych rozmiarach, zarówno automatycznych (na stosie), jak i przydziału statycznego (globalnego lub lokalnego).
W architekturach, które przechowują zarówno stos danych, jak i adresy zwrotne / wskaźniki ramek na stosie (z tego co wiem, to wszystko z nich), każda zmienna przydzielona do stosu może być niebezpieczna, ponieważ można pobrać adres zmiennej, a niesprawdzone wartości wejściowe mogą pozwolić wszelkiego rodzaju psoty.
Przenośność jest mniej istotna w zagnieżdżonej przestrzeni, ale jest to dobry argument przeciwko używaniu funkcji splita () poza ściśle kontrolowanymi okolicznościami.
Poza zagnieżdżoną przestrzenią, w celu zwiększenia wydajności, użyłem przydziałów () głównie w funkcjach rejestrowania i formatowania oraz w nierekurencyjnym skanerze leksykalnym, w którym struktury tymczasowe (przydzielane przy użyciu przydziału () są tworzone podczas tokenizacji i klasyfikacji, a następnie trwałe obiekt (przydzielony przez malloc ()) jest zapełniany przed powrotem funkcji. Użycie funkcji replacea () dla mniejszych struktur tymczasowych znacznie zmniejsza fragmentację, gdy przydzielany jest obiekt trwały.
źródło
Większość odpowiedzi tutaj w dużej mierze nie ma sensu: istnieje powód, dla którego używanie
_alloca()
jest potencjalnie gorsze niż zwykłe przechowywanie dużych obiektów na stosie.Główna różnica między automatycznym przechowywaniem
_alloca()
polega na tym, że ten drugi ma dodatkowy (poważny) problem: przydzielony blok nie jest kontrolowany przez kompilator , więc nie ma możliwości, aby kompilator zoptymalizował go lub poddał recyklingowi.Porównać:
z:
Problem z tym ostatnim powinien być oczywisty.
źródło
alloca
(tak, mówię VLA, ponieważalloca
jest więcej niż tylko twórcą tablic o wielkości statycznej)?Nie sądzę, żeby ktokolwiek o tym wspominał, ale Alfa ma również poważne problemy z bezpieczeństwem, niekoniecznie związane z Malloc (chociaż problemy te pojawiają się także w przypadku tablic opartych na stosie, dynamicznych lub nie). Ponieważ pamięć jest przydzielana na stosie, przepełnienia / niedopełnienia bufora mają znacznie poważniejsze konsekwencje niż w przypadku samego malloc.
W szczególności adres zwrotny funkcji jest przechowywany na stosie. Jeśli ta wartość zostanie uszkodzona, twój kod może przejść do dowolnego wykonywalnego regionu pamięci. Kompilatory bardzo się starają, aby to utrudnić (w szczególności przez losowy układ adresów). Jest to jednak wyraźnie gorsze niż zwykłe przepełnienie stosu, ponieważ najlepszym przypadkiem jest SEGFAULT, jeśli zwracana wartość jest uszkodzona, ale może również zacząć wykonywać losowy fragment pamięci lub, w najgorszym przypadku, pewien obszar pamięci, który zagraża bezpieczeństwu twojego programu .
źródło