Kodėl asigna () nėra laikoma gerąja praktika?

alloca() skiria atmintį ant kamino, o ne krūvos, kaip malloc() . Todėl, kai grįšiu iš įprastinės, atmintis bus atleista. Taigi tai iš tikrųjų išsprendžia mano problemą, susijusią su dinamiškai paskirstytos atminties išlaisvinimu. Atleisdami „ malloc() priskirtą atmintį, yra didelis galvos skausmas ir, jei praleidžiate, tai veda prie visų rūšių atminties problemų.

Kodėl rekomenduojama naudoti alloca() nepaisant pirmiau minėtų funkcijų?

341
19 июня '09 в 19:24 2009-06-19 19:24 „Vaibhav“ yra nustatytas birželio 19 d. 19:24 19:24 2009-06-19 19:24
@ 25 atsakymai

Atsakymas yra teisingas man puslapyje (bent jau Linux ):

RETURN VALUE (ATNAUJINIMO VERTĖ) Assosa () funkcija grąžina žymiklį į priskirtos erdvės pradžią. Jei paskirstymas sukelia kamino perpildymą, programos elgesys yra neapibrėžtas.

Tai nereiškia, kad ji niekada neturėtų būti naudojama. Vienas iš OSS projektų, kuriuos dirbau, yra plačiai naudojamas, o jei nenaudojate piktnaudžiavimo (didelių vertybių), tai gerai. Kai praeisite „kelių šimtų baitų“ ženklą, atėjo laikas naudoti „ malloc ir draugus. Jūs vis dar galite gauti paskirstymo klaidas, bet bent jau turėsite tam tikrą klaidos indikaciją, o ne tik stekų metimą.

200
19 июня '09 в 19:27 2009-06-19 19:27 atsakymą pateikė Sean Bright birželio 19 d., 09:27 19:27 2009-06-19 19:27

Vienas iš labiausiai įsimintinų klaidų buvo tai padaryti su įmontuota funkcija, kuri naudojo alloca . Jis pasireiškė kaip kamino perpildymas (nes jis yra priskirtas kaminai) atsitiktiniuose programos taškuose.

Antraštės faile:

 void DoSomething() { wchar_t* pStr = alloca(100); //...... } 

Įgyvendinimo faile:

 void Process() { for (i = 0; i < 1000000; i++) { DoSomething(); } } 

Taigi, kas atsitiko, buvo įdiegta „ DoSomething kompiliatoriaus funkcija, ir visi stekų paskirstymai buvo atlikti Process() funkcijoje ir taip išpūsti kamino. Mano gynyboje (ir aš ne tas, kuris šį klausimą rado, turėjau eiti ir šaukti vienam iš vyresniųjų kūrėjų, kai negalėjau ją išspręsti), tai nebuvo tiesioginis alloca , tai buvo vienas iš ATL eilutės konversijos makrokomandų.

Taigi pamoka - nenaudokite alloca funkcijose, kurios, jūsų manymu, gali būti įdėtos.

177
05 авг. atsakymą pateikė Igoris Zevaka 05 rug . 2010-08-05 02:35 '10, 2:35 val. 2010-08-05 02:35

Senas klausimas, bet niekas nepaminėjo, kad jis turėtų būti pakeistas kintamo ilgio matricomis.

 char arr[size]; 

vietoj

 char *arr=alloca(size); 

Jis yra C99 standarte ir egzistavo kaip kompiliatoriaus plėtinys daugelyje kompiliatorių.

63
01 авг. Atsakymą pateikė Patrick Schlüter 01 rug. 2010-08-01 23:45 '10, 23:45, 2010-08-01 23:45

asigna () yra labai naudinga, jei negalite naudoti standartinio vietinio kintamojo, nes jo dydis turi būti nustatytas vykdymo metu, ir jūs galite visiškai garantuoti, kad rodiklis, kurį gausite iš asigna (), niekada nebus naudojamas po šios funkcijos grąžinimo .

Jūs galite būti pakankamai saugūs, jei jūs

  • negrąžinkite rodyklės ar nieko, kurioje yra.
  • nelaikykite rodyklės jokioje struktūroje, pasirinktoje krūvoje
  • neleiskite niekam kitam naudoti žymeklio

Tikrasis pavojus kyla iš tikimybės, kad kažkas vėliau pažeidžia šias sąlygas. Turint tai omenyje, puikiai tinka perduoti buferius funkcijoms, kurios formuoja tekstą jose :)

52
23 июня '09 в 2:46 2009-06-23 02:46 atsakymą pateikė Arthur Ulfeldt birželio 23 d., 09:46 , 2009-06-23 02:46

Kaip nurodyta šiame leidinyje naujienų grupėje , yra keletas priežasčių, dėl kurių alloca gali būti laikomas sudėtingu ir pavojingu:

  • Ne visi kompiliatoriai palaiko alloca .
  • Kai kurie kompiliatoriai skirtingai interpretuoja numatomą lėšų panaudojimą, todėl perkeliamumas nėra garantuojamas netgi tarp jų palaikančių kompiliatorių.
  • Kai kurie diegimai yra klaidingi.
37
19 июня '09 в 19:28 2009-06-19 19:28 atsakymą pateikė „ FreeMemory“ birželio 19 d. , 09:28, 2009-06-19 19:28

Viena problema yra ta, kad ji nėra standartinė, nors ir plačiai remiama. Kiti dalykai, lygūs, visada naudoju standartinę funkciją, o ne bendrąjį kompiliatoriaus išplėtimą.

25
19 июня '09 в 19:35 2009-06-19 19:35 Atsakymą davė David Thornley birželio 19 d. 19:35 19:35 2009-06-19 19:35

vis dar nėra rekomenduojama naudoti, kodėl?

Nesuprantu tokio sutarimo. Daug stiprių privalumų; keli trūkumai:

  • C99 suteikia kintamo ilgio matricas, kurios dažnai naudojamos pirmiausia, nes žymėjimas labiau atitinka fiksuoto ilgio matricas ir intuityvų bendrą
  • daugelis sistemų turi mažiausiai atminties / adresų vietos užkrovimui, nei kasykla, todėl programa yra šiek tiek jautresnė atminties išsekimui (per stekų perpildymą): tai gali būti laikoma geru ar blogu dalyku - viena iš priežasčių, kodėl kaminai nėra automatinė auga kaip krūva, kad būtų užkirstas kelias nekontroliuojamam valdymo programų naudojimui per visą mašiną.
  • kai naudojama vietinėje vietovėje (pvz., while ar while ) arba keliose atminties srityse, atmintis kaupiama kiekvienam iteracijai / apimčiai ir neatleidžiama tol, kol nebus paleista funkcija: tai prieštarauja tam tikriems valdymo struktūros kintamiesiems (pvz., for {int i = 0; i < 2; ++i) { X } sukauptų for {int i = 0; i < 2; ++i) { X } atmintyje reikalaujamą atmintį, tačiau fiksuoto dydžio masyvo atmintis bus apdorojama iteracija).
  • Šiuolaikiniai kompiliatoriai paprastai neatlieka inline funkcijų, kurios skambina alloca , bet, jei jas priverčiate, alloca atsiras skambintojų kontekste (t. Y. Stekas nebus išleistas iki skambintojo grąžinimo)
  • seniai, alloca persikėlė iš nepakeliamos funkcijos / įsilaužimo į standartizuotą pratęsimą, tačiau tam tikras neigiamas suvokimas gali išlikti
  • gyvenimo trukmė yra susieta su funkcijos, kuri gali arba netinka programuotojui, apimtimi geriau nei „ malloc aiški kontrolė
  • naudoti „ malloc , tai verčia galvoti apie atleidimą - jei ji valdoma per įvyniojimo funkciją (pvz., WonderfulObject_DestructorFree(ptr) ), tada funkcija suteikia tašką valymo operacijoms (pvz., uždaryti failų rankenas, atleisti vidinius rodykles arba vykdyti kai kuriuos protokolus) be aiškių pakeitimų kliento kodas: kartais tai geras nuoseklaus priėmimo modelis
    • šiame pseudo-OO stiliaus programavime, žinoma, norite kažką panašaus į WonderfulObject* p = WonderfulObject_AllocConstructor(); - tai įmanoma, kai „konstruktorius“ yra funkcija, kuri grąžina atmintį į „ malloc -ed“ (kadangi atmintis išlieka paskirta po to, kai funkcija grąžina vertę, kuri turėtų būti saugoma p ), bet ne tada, jei „konstruktorius“ naudojasi alloca
      • WonderfulObject_AllocConstructor “ makro versija gali tai pasiekti, tačiau „makrokomandos yra blogos“, nes jos gali prieštarauti viena kitai ir ne makrokodui ir sukurti nenumatytus pakeitimus, todėl sunku diagnozuoti problemas.
    • Trūksta free operacijų galima aptikti „ValGrind“, „Purify“ ir tt, tačiau trūkstamų „destruktorių“ skambučių ne visada galima aptikti - vienas labai nedidelis pranašumas užtikrinant numatomą naudojimą; kai kurie alloca() (pvz., GCC) diegimai naudoja įmontuotą makrokomandą alloca() , todėl atminties bibliotekos realloc skirtas naudoti vykdymo metu, yra neįmanomas, kaip tai daroma malloc / realloc / free (pavyzdžiui, elektrinė tvora).
  • Kai kurie diegimai turi subtilių problemų: pvz., Iš „Linux“ žmogaus puslapio:

    Daugelyje sistemų asigna () negali būti naudojama funkcijų skambučio argumentų sąrašo viduje, nes dedeklių () rezervuota kamino vieta bus rodoma kamino erdvės viduryje funkcijų argumentams.


Žinau, kad šis klausimas pažymėtas C, bet, kaip C ++ programuotojas, aš maniau, kad naudoju C + +, kad galėčiau iliustruoti potencialų alloca įrankį: toliau pateikiamas kodas (ir čia ideone ) sukuria įvairių dydžių polimorfinių tipų, kuriuos skiria kaminai, sekimą. (atsižvelgiant į gyvenimo trukmę, kad grąžintumėte funkciją), o ne specialią krūvą.

 #include <alloca.h> #include <iostream> #include <vector> struct Base { virtual ~Base() { } virtual int to_int() const = 0; }; struct Integer : Base { Integer(int n) : n_(n) { } int to_int() const { return n_; } int n_; }; struct Double : Base { Double(double n) : n_(n) { } int to_int() const { return -n_; } double n_; }; inline Base* factory(double d) __attribute__((always_inline)); inline Base* factory(double d) { if ((double)(int)d != d) return new (alloca(sizeof(Double))) Double(d); else return new (alloca(sizeof(Integer))) Integer(d); } int main() { std::vector<Base*> numbers; numbers.push_back(factory(29.3)); numbers.push_back(factory(29)); numbers.push_back(factory(7.1)); numbers.push_back(factory(2)); numbers.push_back(factory(231.0)); for (std::vector<Base*>::const_iterator i = numbers.begin(); i != numbers.end(); ++i) { std::cout << *i << ' ' << (*i)->to_int() << '\n'; (*i)->~Base(); // optionally / else Undefined Behaviour iff the // program depends on side effects of destructor } } 
21
26 февр. atsakymą pateikė Tony Delroy 26 vasario mėn. 2013-02-26 13:31 '13, 13:31, 2013-02-26 13:31

Visi kiti atsakymai yra teisingi. Tačiau, jei dalykas, kurį norite skirti su alloca() yra pakankamai mažas, manau, kad tai yra geras būdas, kuris yra greitesnis ir patogesnis nei naudojant malloc() ar kitaip.

Kitaip tariant, alloca( 0x00ffffff ) pavojinga ir gali sukelti perpildymą, lygiai taip pat, kaip ir char hugeArray[ 0x00ffffff ]; . Būkite atsargūs ir protingi, ir viskas bus gerai.

11
19 июня '09 в 19:32 2009-06-19 19:32 atsakymą pateikė JSB ձոգչ09 birželio 19 d. 19:32 19:32

Kiekvienas jau nurodė didelį dalyką, kuris yra potencialus elgesys, kurio neapibrėžta, bet turiu paminėti, kad „Windows“ aplinkoje yra didelis mechanizmas, kad jį sugautų naudojant struktūruotas išimtis (SEH) ir saugumo puslapius. Kadangi krūva auga tik tada, kai reikia, šie saugos puslapiai yra nepaskirstytose srityse. Jei pasirinksite juos (pagal), sukuriama išimtis.

Galite sulaikyti šią SEH išimtį ir paskambinti _resetstkoflw, kad iš naujo nustatytumėte steką ir tęstumėte linksmą kelionę. Tai nėra tobula, bet kitas mechanizmas, bent jau žinant, kažkas negerai, kai medžiaga patenka į ventiliatorių. * nix gali turėti kažką panašaus, apie kurį aš nežinau.

Aš rekomenduoju apriboti maksimalų pasirinkimo dydį įvyniuodamas ir padedant jį sekti. Jei buvote labai sunkūs, savo funkcijos viršuje galite mesti žiūrėti vaizdus, ​​kad galėtumėte stebėti visus asignavimus, susijusius su funkcijomis ir protingumu, išbandyti jį pagal maksimalią jūsų projektui leistiną sumą.

Be to, ne tik užkirsti kelią atminties nutekėjimui, bet ir nesuteikia atminties fragmentacijos, kuris yra labai svarbus. Nemanau, kad asigna yra bloga praktika, jei ją išmintingai naudojate, o tai iš esmės tinka visoms: -)

11
21 марта '11 в 19:19 2011-03-21 19:19 atsakymas į „ SilentDirge“ pateikiamas kovo 11 d. 11 val. 19:19 2011-03-21 19:19

Yra daug įdomių atsakymų į šį „senąjį“ klausimą, net ir kai kuriuos palyginti naujus atsakymus, bet aš to nepaminėčiau.

Naudojant tinkamai ir atsargiai, naudodamiesi alloca() nuosekliai (galbūt visai programai) mažiems kintamojo ilgio paskirstymams (arba C99 VLA, jei jie yra), gali būti mažesnis augimo dydis nei lygiavertis įgyvendinimas naudojant per didelius fiksuoto ilgio vietinius matricus. Todėl, jei naudosite jį atsargiai, alloca() gali būti gera .

Radau šią citatą .... Na, aš padariau šią citatą. Bet tikrai, pagalvokite apie tai.

@j_random_hacker yra labai teisus savo pastabose dėl kitų atsakymų: vengiant alloca() naudos didelėms vietinėms matricoms jūsų programa nėra saugesnė (jei kompiliatorius nėra pakankamai senas, kad būtų galima įterpti funkcijas, kurios naudoja alloca() , šiame Tokiu atveju turėtumėte atnaujinti arba, jei nenaudojate vidinių alloca() ciklų, tokiu atveju neturėtumėte naudoti vidinių ciklų alloca() .

Dirbu darbastalio / serverio aplinkose ir įterptinėse sistemose. Daugelis įterptųjų sistemų visai nesinaudoja krūva (jie net nenurodo paramos) dėl priežasčių, kurios apima suvokimą, kad dinamiškai paskirstyta atmintis yra bloga dėl atminties nutekėjimo pavojaus programoje, kuri niekada nebuvo perkrauta daugelį metų. vienu metu arba racionalesniu pagrindu tam, kad dinamiška atmintis yra pavojinga, nes nėra žinoma, kad programa niekada nesudaro fragmento į klaidingos atminties išsekimo lygį. Todėl įterptieji programuotojai liko keliomis alternatyvomis.

asigna alloca() (arba VLA) gali būti tinkama priemonė darbui.

Aš vėl ir vėl pastebėjau, kur programuotojas sukuria buferį, kuris yra paskirstytas per stelažus, „pakankamai didelis, kad galėtume tvarkyti bet kokį atvejį“. Į giliai įdėtą skambučių medį, pakartotinai panaudojant šį šabloną (anti-?), Perduodama pernelyg didelė krūva. (Įsivaizduokite, kad skambučių medis yra 20 lygių giliai, kur kiekviename lygyje dėl įvairių priežasčių funkcija aklai 1024 baito buferį aklai perskirsto tik tam, kad būtų saugus, kai paprastai jis naudoja tik 16 ar mažiau iš jų ir tik Labai retais atvejais gali būti naudojama daugiau.) Alternatyva yra naudoti alloca() arba VLA ir paskirstyti tik tiek daug kamino vietos, nes jūsų funkcijai reikia išvengti nereikalingos apkrovos. Tikimės, kad kai viena skambučio medžio funkcija turi platinti daugiau nei įprasta, kiti skambučių medyje esantys asmenys vis dar naudoja savo įprastus nedidelius paskirstymus, o bendras taikomųjų programų paketo naudojimas yra daug mažesnis nei tuo atveju, jei kiekviena funkcija aklai pernelyg paskirstytų vietinį buferį.

Bet jei nuspręsite naudoti alloca() ...

Remiantis kitais šiame puslapyje pateiktais atsakymais, atrodo, kad VLA turi būti saugūs (jie nesuderina stekų paskyrimų, jei jie yra skambinami iš kilpos), tačiau, jei naudojate alloca() , būkite atsargūs, kad nenaudotumėtės toje linijoje ir įsitikinkite, kad Funkcija negali būti įterpta, jei tikėtina, kad ją galima pavadinti kitu funkcijų ciklu.

9
01 апр. atsakymą pateikė phonetagger 01 balandžio. 2016-04-01 01:00 '16 at 1:00 2016-04-01 01:00

Štai kodėl:

 char x; char *y=malloc(1); char *z=alloca( *z = 1; 

Ne tai, kad kas nors rašo šį kodą, bet dydžio argumentas, kurį perduodate alloca beveik neabejotinai kyla iš tam tikros informacijos, kuri gali netinkamai padaryti jūsų programai kažką didžiulio. Galų gale, jei dydis nėra pagrįstas įvesties duomenimis arba jis neturi didelio pajėgumo, kodėl jūs ne tik paskelbėte nedidelį fiksuoto dydžio buferį?

Beveik visi kodai, naudojami naudojant „a“ ir „C99 vlas“, turi rimtų klaidų, kurios sukels gedimus (jei būsite laimingi) arba kompromisų dėl privilegijų (jei nesate laimingi).

9
01 авг. Atsakymas yra R .. 01 rugpjūtis 2010-08-01 23:32 '10 ne 23:32 2010-08-01 23:32

asigna () yra gera ir veiksminga ... tačiau ji taip pat labai sulaužyta.

  • elgesys su pakeista taikymo sritimi (taikymo sritis vietoj blokų apimties)
  • naudoti nesuderinamą su malloc ( asigna ()) Nurodytas rodiklis neturėtų būti atlaisvintas, nuo šiol turėtumėte sekti, kur rodyklės eina iš nemokamų () , kurių jūs turite su malloc ( )
  • blogas elgesys, kai taip pat naudojate įterpimą (kartais perėjimas į skambinančiojo funkciją, priklausomai nuo to, ar prašymas sukonfigūruotas, ar ne).
  • patikrinimas
  • neapibrėžtas elgesys gedimo atveju (negrąžina NULL, kaip malloc ... ir tai reiškia gedimą, nes jis jokiu būdu nekontroliuoja kamino ribų ...)
  • ne standarto standartas

Daugeliu atvejų galite jį pakeisti vietiniais kintamaisiais ir pagrindinio dydžio dydžiu. Jei jis naudojamas dideliems objektams, jų įdėjimas į krūvą paprastai yra saugesnė idėja.

Jei jums tai tikrai reikia, galite naudoti VLA (ne vla C ++, per blogai). Jie yra gerokai geresni už paskirstymą (), susijusius su teritorijos elgesiu ir nuoseklumu. Kaip matau, VLA yra tam tikras paskirstymas () .

Žinoma, vietinė struktūra ar masyvas, naudojant reikiamą erdvę, yra dar geresnis, o jei neturite tokio didelio krūvio paskirstymo naudojant paprastą malloc (), tai tikriausiai yra pagrįsta. Aš nematau tinkamo naudojimo atvejo, kai jums tikrai reikia arba asigna (), arba VLA.

9
02 сент. atsakymas suteiktas kriss 02 sep . 2014-09-02 13:26 '14, 13:26 2014-09-02 13:26

Vieta, kur alloca() ypač pavojinga nei malloc() - branduolys - tipinės operacinės sistemos šerdis turi fiksuotą kamino erdvę, kuri yra sunkiai koduojama į vieną iš jos antraštių; jis nėra toks lankstus, kaip taikomoji programa. Skambinimas su nepagrįstu dydžiu gali sukelti branduolio gedimą. Kai kurie kompiliatoriai įspėja apie alloca() (ir net VLA) naudojimą tam tikrais parametrais, kurie turi būti įtraukti į branduolio kodo sudarymą. Čia geriau atminties priskirti krūvoms, kurios nėra fiksuotos pagal standžią konfigūraciją, koduotą ribą.

7
27 нояб. Sondhi Chakraborty lapkričio 27 d 2010-11-27 21:49 '10, 09:49 PM 2010-11-27 21:49

Deja, iš tikrųjų neįtikėtinas alloca() trūksta beveik siaubingame tcc. Gcc turi asigna alloca() .

  • Jis sėja savo sunaikinimo sėklą. Su grįžimu kaip destruktorius.

  • Kaip malloc() , jis sugrąžina neteisingą rodyklę, kad jis nepavyktų, kuris bus segmentuojamas šiuolaikinėse sistemose su MMU (ir, tikiuosi, iš naujo juos paleisiu).

  • Skirtingai nuo automatinių kintamųjų, galite nurodyti dydį vykdymo metu.

Tai gerai veikia su rekursija. Galite naudoti statinius kintamuosius, kad pasiektumėte kažką panašaus į uodegos rekursiją ir naudokite tik keletą kitų, kad perduotumėte informaciją kiekvienam iteracijai.

Jei paspausite pernelyg giliai, esate tikri, kad segmentas (jei turite MMU).

Atkreipkite dėmesį, kad malloc() nebesuteikia, nes ji grąžina NULL (kuris taip pat bus segfault, jei jis bus priskirtas), kai sistema bus išjungta. То есть все, что вы можете сделать, это залог или просто попытаться назначить его любым способом.

Чтобы использовать malloc() , я использую глобальные переменные и назначаю их NULL. Если указатель не равен NULL, я освобожу его, прежде чем использовать malloc() .

Вы также можете использовать realloc() в качестве общего случая, если хотите скопировать любые существующие данные. Перед тем, как работать, вам нужно проверить указатель, если вы собираетесь копировать или конкатенировать после realloc() .

3.2.5.2 Преимущества alloca

4
ответ дан zagam 30 марта '11 в 9:58 2011-03-30 09:58