Neden alloca () kullanımı iyi uygulama olarak kabul edilmiyor?


400

alloca() bellekte olduğu gibi yığın yerine bellek ayırır malloc() . Böylece, rutinden döndüğümde bellek boşalır. Yani, aslında bu, dinamik olarak ayrılmış belleği boşaltma sorunumu çözdü. Ayrılan belleğin boşaltılması malloc()büyük bir baş ağrısıdır ve bir şekilde kaçırılırsa her türlü bellek sorununa yol açar.

alloca()Yukarıdaki özelliklere rağmen neden cesaretiniz kırılıyor?


40
Sadece hızlı bir not. Bu işlev çoğu derleyicide bulunabilse de ANSI-C standardı için gerekli değildir ve bu nedenle taşınabilirliği sınırlayabilir. Başka bir şey, yapmamalısınız! free () işaretçisini alırsınız ve işlevden çıktıktan sonra otomatik olarak serbest bırakılır.
merkuro

9
Ayrıca, böyle bildirilirse alloca () işlevine sahip bir satır çizilmez.
Justicle

2
@ Justicle, biraz açıklama sağlayabilir? Bu davranışın arkasında ne olduğunu çok merak ediyorum
migajek

47
Taşınabilirlik, aramaya gerek yok free(ki bu açıkça bir avantajdır), satır içi yapamama (açıkçası yığın tahsisleri çok daha ağır) vb. Tüm gürültüyü unutun alloca. Yani, tonlarca yığın belleğin boşa harcanması iyi bir fikir değildir, ayrıca bir yığın taşması şansınız vardır. Bu durumda - malloca/freea
valdo

5
Diğer bir olumlu yönü alloca, yığının yığın gibi parçalanamamasıdır. WCRU daha sonra kendi sorun kümelerine sahip özel bellek havuzlarına başvurmadan statik olarak analiz edilebildiğinden (gerçek zamanlı konum, en uygun kaynak yok) bu gerçek zamanlı sürekli çalışma tarzı uygulamalar ve hatta güvenlik açısından kritik uygulamalar için yararlı olabilir. kullanımı).
Andreas

Yanıtlar:


245

Cevap tam mansayfada (en azından Linux'ta ):

DÖNÜŞ DEĞERİ alloca () işlevi, ayrılan alanın başına bir işaretçi döndürür. Ayırma yığın taşmasına neden oluyorsa, program davranışı tanımsızdır.

Bu asla kullanılmamalıdır demek değildir. Üzerinde çalıştığım OSS projelerinden biri onu yoğun bir şekilde kullanıyor ve kötüye kullanmadığınız sürece ( allocabüyük değerler), sorun değil. "Birkaç yüz bayt" işaretini geçtikten sonra, kullanma zamanı mallocve arkadaşlar. Yine de ayırma hataları alabilirsiniz, ancak en azından sadece yığını havaya uçurmak yerine hataya ilişkin bazı belirtilere sahip olacaksınız.


35
Yani, büyük dizileri bildirmekle de karşılaşmayacağınız bir sorun yok mu?
TED

88
@Sean: Evet, yığın taşması riski verilen nedendir, ancak bu sebep biraz saçmadır. Birincisi (Vaibhav'un ​​dediği gibi) büyük yerel diziler tam olarak aynı soruna neden olur, ancak neredeyse aynı şekilde aşağılanmış değildir. Ayrıca, özyineleme de yığını kolayca üfleyebilir. Üzgünüm ama umarım man sayfasında verilen nedenin haklı olduğu fikrine karşı gelmek için -1 diyorum.
j_random_hacker

49
Demek istediğim, man sayfasında verilen gerekçenin hiçbir anlamı yok, çünkü alloca (), koşer olarak kabul edilen diğer şeyler (yerel diziler veya özyinelemeli fonksiyonlar) kadar "kötü".
j_random_hacker

39
@ninjalj: Son derece deneyimli C / C ++ programcıları tarafından değil, ancak korkan birçok kişinin alloca()aynı yerel diziler veya özyineleme korkusuna sahip olmadığını düşünüyorum (aslında bağıracak birçok insan alloca()"zarif görünüyor" için özyineyi övecek ) . Shaun'un tavsiyesi ile ("alloca () küçük tahsisler için iyidir") katılıyorum ama alloca () 3 arasında benzersiz kötü çerçeveler zihniyet ile katılmıyorum - eşit derecede tehlikelidir!
j_random_hacker

35
Not: Linux'un "iyimser" bellek ayırma stratejisi göz önüne alındığında , bir yığın tükenme hatası belirtisi alamazsınız ... bunun yerine malloc () size güzel bir NULL olmayan işaretçi döndürür ve sonra gerçekten işaret ettiği adres alanına erişirseniz, işleminiz (veya öngörülemeyen başka bir işlem) OOM katili tarafından öldürülecektir. Tabii ki bu bir C / C ++ sorunu yerine Linux'un bir "özelliğidir", ancak alloca () veya malloc () 'un "daha güvenli" olup olmadığını tartışırken akılda tutulması gereken bir şeydir. :)
Jeremy Friesner

209

Sahip olduğum en unutulmaz hatalardan biri, kullanılan bir satır içi işlev ile ilgiliydi alloca. Kendisini, programın yürütülmesinin rasgele noktalarında bir yığın taşması olarak gösterdi (yığına ayırdığı için).

Başlık dosyasında:

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

Uygulama dosyasında:

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

Böylece, derleyici satır içi DoSomethingişlevi ve tüm yığın tahsisleri Process()işlev içinde gerçekleşiyordu ve böylece yığını havaya uçuruyordu. Savunmamda (ve sorunu bulan kişi ben değildim; düzeltemediğimde üst düzey geliştiricilerden birine gitmek zorunda kaldım), düz değildi alloca, ATL string dönüşümünden biriydi makrolar.

Yani ders - allocaeğik olabileceğini düşündüğünüz işlevlerde kullanmayın .


91
İlginç. Ama bu bir derleyici hatası olarak nitelendirilmez mi? Sonuçta, satır içi kodun davranışını değiştirdi (alloca kullanılarak ayrılan alanın serbest bırakılmasını geciktirdi).
sleske

60
Görünüşe göre, en azından GCC bunu dikkate alacaktır: "Bir işlev tanımındaki bazı kullanımların onu satır içi ikame için uygun hale getirebileceğini unutmayın. Bu kullanımlar arasında: varargs kullanımı, alloca kullanımı, [...]". gcc.gnu.org/onlinedocs/gcc/Inline.html
sleske

136
Hangi derleyiciyi içiyordunuz?
Thomas Eding

22
Anlamadığım şey, derleyicinin altboptaki allocasların az ya da çok "serbest" olduğunu belirlemek için neden kapsamı iyi kullanmamasıdır: yığın işaretçisi kapsama girmeden önce noktasına geri dönebilir, örneğin ne zaman yapılır işlevden dönen (yapamadı mı?)
moala

7
Aşağı düştüm, ama cevap iyi yazılmış: Açıkça bir derleyici hatası için alloca arızalı başkalarına katılıyorum . Derleyici, yapmaması gereken bir optimizasyonda hatalı bir varsayımda bulundu. Bir derleyici hata etrafında çalışmak iyidir, ama bunun için derleyici dışında bir şey hata olmaz.
Evan Carroll

75

Eski soru ama kimse bunun değişken uzunluklu dizilerle değiştirilmesi gerektiğini söylemedi.

char arr[size];

onun yerine

char *arr=alloca(size);

Standart C99'da ve birçok derleyicide derleyici uzantısı olarak mevcuttu.


5
Arthur Ulfeldt'in cevabına yaptığı bir yorumda Jonathan Leffler tarafından bahsedildi.
ninjalj

2
Gerçekten de, göndermeden önce tüm yanıtları okumaya rağmen görmediğim için, ne kadar kolay kaçırıldığını da gösterir.
Patrick Schlüter

6
Bir not - bunlar dinamik diziler değil, değişken uzunluklu dizilerdir. İkincisi yeniden boyutlandırılabilir ve genellikle öbek üzerinde uygulanır.
Tim Čas

1
Bazı C ++ derleyen Visual Studio 2015 de aynı soruna sahiptir.
ahcox

2
Linus Torvalds , Linux çekirdeğindeki VLA'ları sevmiyor . Sürüm 4.20 itibariyle Linux neredeyse VLA içermemelidir.
Cristian Ciupitu

60

Standart bir yerel değişkeni kullanamıyorsanız alloca () çok kullanışlıdır, çünkü boyutunun çalışma zamanında belirlenmesi gerekir ve alloca () 'dan aldığınız işaretçinin bu işlev döndükten sonra ASLA kullanılmayacağını kesinlikle garanti edebilirsiniz .

Eğer sen oldukça güvende olabilirsin

  • işaretçiyi veya onu içeren herhangi bir şeyi döndürmeyin.
  • işaretçiyi öbek üzerinde ayrılmış herhangi bir yapıda saklamayın
  • başka bir iş parçacığının işaretçiyi kullanmasına izin verme

Gerçek tehlike, bir başkasının bu koşulları bir süre sonra ihlal etme şansından kaynaklanmaktadır. Bunu akılda tutarak, tamponları metni biçimlendiren işlevlere geçirmek için harika :)


12
C99'un VLA (değişken uzunluk dizisi) özelliği, açıkça alloca () kullanılmasına gerek kalmadan dinamik olarak boyutlandırılmış yerel değişkenleri destekler.
Jonathan Leffler


1
Ancak bu, işaretçilerle yerel değişkenlere yönelmekten farklı değildir. Etrafında da kandırılabilirler ...
glglgl

2
@Jonathan Leffler alloca ile yapabileceğiniz bir şey ama VLA ile yapamayacağınız onlarla restrict anahtar kelime kullanıyor. Bunun gibi: float * restrict heavily_used_arr = alloca (sizeof (float) * boyut); float yerine heavily_used_arr [size]. Bazı derleyiciler (benim durumumda gcc 4.8), boyut bir derleme sabiti olsa bile montajı optimize etmeye yardımcı olabilir. Bununla ilgili soruma bakın: stackoverflow.com/questions/19026643/using-restrict-with-arrays
Piotr Lopusiewicz

@JonathanLeffler Bir VLA, onu içeren blokta yereldir. Öte yandan, alloca()işlevin sonuna kadar süren belleği ayırır. Bu demektir ki, VLA için basit ve uygun bir çeviri yoktur f() { char *p; if (c) { /* compute x */ p = alloca(x); } else { p = 0; } /* use p */ }. allocaKullanımları VLA kullanımlarına otomatik olarak çevirmenin mümkün olduğunu düşünüyorsanız, ancak nasıl yapılacağını açıklamak için bir yorumdan daha fazlasına ihtiyacınız varsa, bunu bir soru haline getirebilirim.
Pascal Cuoq

40

Bu haber grubu gönderisinde belirtildiği gibi alloca, kullanımın zor ve tehlikeli olarak kabul edilmesinin birkaç nedeni vardır :

  • Tüm derleyiciler desteklenmez alloca.
  • Bazı derleyiciler amaçlanan davranışı allocafarklı şekilde yorumlarlar, bu nedenle onu destekleyen derleyiciler arasında bile taşınabilirlik garanti edilmez.
  • Bazı uygulamalar hatalıdır.

24
Bu sayfada başka bir yerde olmayan bu bağlantıda bahsettiğim bir şey, kullanan bir fonksiyonun alloca()yığın işaretçisini ve çerçeve işaretçisini tutmak için ayrı kayıtlar gerektirmesidir. X86 CPU'larda> = 386, yığın işaretçisi ESPkullanılmadığı EBPsürece her ikisinde de alloca()kullanılabilir.
j_random_hacker

10
Bu sayfadaki bir başka iyi nokta, derleyicinin kod üreteci bunu özel bir durum olarak ele almadığı sürece, f(42, alloca(10), 43);yığın işaretçisinin argümanlardan en az birine itildikten alloca() sonra ayarlanması olasılığı nedeniyle çökebilir .
j_random_hacker

3
Bağlantılı yazı John Levine tarafından yazılmış gibi görünüyor - "Linkers and Loaders" yazan dostum, ne derse desin güvenirim.
user318904

3
Bağlantılı sonrası bir olan cevap John Levine tarafından bir gönderme için.
A. Wilcox

6
Unutmayın, 1991'den beri çok şey değişti. Tüm modern C derleyicileri (2009'da bile) alloka'yı özel bir durum olarak ele almak zorundadır; sıradan bir işlevden ziyade içsel bir işlevdir ve bir işlev bile çağırmayabilir. Bu nedenle, (1970'lerden itibaren K&R C'de ortaya çıkan) parametre içindeki alloka sorunu artık bir sorun olmamalıdır. Tony D'nin cevabı hakkında yaptığım yorumda daha fazla ayrıntı
greggo

26

Bir sorun, yaygın olarak desteklenmesine rağmen standart olmamasıdır. Diğer şeyler eşit olduğunda, her zaman ortak bir derleyici uzantısı yerine standart bir işlev kullanırdım.


21

hala alloca kullanımı önerilmez, neden?

Böyle bir fikir birliği algılamıyorum. Çok güçlü profesyoneller; birkaç eksileri:

  • C99, gösterimin sabit uzunluklu diziler ve genel olarak sezgisel olarak daha tutarlı olması nedeniyle tercihli olarak kullanılacak değişken uzunluklu diziler sağlar
  • birçok sistem, yığın için kullanılabilir olandan daha az genel bellek / adres alanına sahiptir, bu da programı bellek tükenmesine (yığın taşması yoluyla) biraz daha duyarlı hale getirir: bu iyi veya kötü bir şey olarak görülebilir - bir yığının yığının yaptığı gibi otomatik olarak büyümemesinin nedenleri, kontrol dışı programların tüm makine üzerinde çok fazla olumsuz etkiye sahip olmasını önlemektir.
  • daha yerel bir kapsamda (a whileveya fordöngü gibi) veya birkaç kapsamda kullanıldığında, bellek yineleme / kapsam başına birikir ve işlev çıkana kadar serbest bırakılmaz: bu, bir kontrol yapısı kapsamında tanımlanan normal değişkenlerle (ör. for {int i = 0; i < 2; ++i) { X }birikiralloca X'de istenen -ed belleği , ancak sabit boyutlu bir dizi için bellek yineleme başına geri dönüştürülür).
  • modern derleyiciler genellikle inlineçağrı işlevi görmez alloca, ancak onları zorlarsanız alloca, çağrıcının bağlamında gerçekleşir (yani, arayan geri dönene kadar yığın serbest bırakılmaz)
  • uzun zaman önce allocataşınabilir olmayan bir özellikten / bilgisayar korsanlığından Standartlaştırılmış bir uzantıya geçildi, ancak bazı olumsuz algılamalar devam edebilir
  • kullanım ömrü, programlayıcıya mallocaçık kontrolden daha iyi uyup uymayabilecek fonksiyon kapsamına bağlıdır.
  • kullanmak zorunda mallocbırakma hakkında düşünmeyi teşvik eder - eğer bir sarmalayıcı işlevi (örneğin WonderfulObject_DestructorFree(ptr)) aracılığıyla yönetiliyorsa , bu işlev istemcide açıkça değişiklikler yapılmadan uygulama temizleme işlemleri (dosya tanımlayıcılarını kapatma, dahili işaretleyicileri serbest bırakma veya bazı günlüğe kaydetme gibi) için bir nokta sağlar kod: bazen tutarlı bir şekilde benimsemek güzel bir model
    • bu sözde OO programlama tarzında, böyle bir şey istemek doğaldır WonderfulObject* p = WonderfulObject_AllocConstructor();- "yapıcı", malloc-ed bellek döndüren bir işlev olduğunda mümkündür (işlev, saklanacak değeri döndürdükten sonra bellek ayrılmış olarak kalır p), ancak "kurucu"alloca
      • bir makro sürümü WonderfulObject_AllocConstructorbunu başarabilir, ancak "makrolar kötüdür", çünkü birbirleriyle ve makro olmayan kodlarla çakışabilirler ve istenmeyen ikameler yaratırlar ve sonuç olarak teşhis edilmesi zor problemler oluştururlar.
    • eksik freeişlemler ValGrind, Purify vb. tarafından tespit edilebilir, ancak eksik "yıkıcı" çağrılar her zaman hiç tespit edilemez - amaçlanan kullanımın uygulanması açısından çok sürekli bir fayda; bazı alloca()uygulamalar (GCC'ler gibi) için satır içi bir makro kullanır alloca(), bu nedenle bellek kullanımı tanı kitaplığının çalışma zamanının değiştirilmesi malloc/ realloc/ free(örneğin elektrikli çit)
  • bazı uygulamaların ince sorunları vardır: örneğin, Linux kılavuzundan:

    Birçok sistemde alloca () işlev çağrısının bağımsız değişkenleri listesinde kullanılamaz, çünkü alloca () tarafından ayrılan yığın alanı, işlev bağımsız değişkenleri için alanın ortasında yığında görünür.


Bu soru C etiketli biliyorum, ama bir C ++ programcısı olarak ben potansiyel kullanımı göstermek için C ++ kullanacağını düşündüm alloca: aşağıdaki kod (ve burada ideone ) yığını ayrılmış (boyutlu ile) Ömür boyu işlev dönüşüne bağlı).

#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
    }
}

hayır + 1
einpoklum

@ einpoklum: bu çok aydınlatıcı ... teşekkürler.
Tony Delroy

1
Yeniden ifade edeyim: Bu çok iyi bir cevap. İnsanların bir çeşit karşı kalıp kullanmasını önerdiğiniz noktaya kadar.
einpoklum

Linux manpage'in yorumu çok eski ve eminim eski. Tüm modern derleyiciler alloca'nın () ne olduğunu biliyorlar ve böyle ayakkabı bağcığı üzerinden geçmeyecekler. Eski K&R C'de, (1) tüm fonksiyonlar çerçeve işaretçileri (2) kullandı. Tüm fonksiyon çağrıları {yığıntaki push args} {call func} {add # n, sp} idi. alloca yığınının üstünü kıracak bir lib fonksiyonuydu, derleyici bunun olduğunu bile bilmiyordu. (1) ve (2) artık doğru değil, bu yüzden alloca bu şekilde çalışamaz (şimdi içseldir). Eski C'de, argümanları itmenin ortasında alloca çağırmak açıkça bu varsayımları kıracaktır.
greggo

4
Örnek ile ilgili olarak, genellikle bellek bozulmasını önlemek için always_inline gerektiren bir şey hakkında endişe ediyorum ....
greggo

14

Diğer tüm cevaplar doğrudur. Bununla birlikte, kullanarak ayırmak istediğiniz şey alloca()oldukça küçükse, kullanmaktan daha hızlı ve daha uygun olan iyi bir teknik olduğunu düşünüyorum malloc().

Başka bir deyişle, alloca( 0x00ffffff )tehlikelidir ve tam olarak olduğu gibi taşmaya neden olabilir char hugeArray[ 0x00ffffff ];. Dikkatli ve makul olun ve iyi olacaksınız.


12

Bu "eski" soru ilginç cevaplar, hatta bazı nispeten yeni cevaplar, ama bu söz herhangi bulamadık ....

Düzgün ve özenle kullanıldığında, alloca() küçük değişken uzunluklu ayırmaları (veya varsa C99 VLA'ları) işlemek için (belki de uygulama çapında) tutarlı kullanımı , sabit uzunluktaki büyük boyutlu yerel diziler kullanılarak, aksi takdirde eşdeğer bir uygulamaya göre daha düşük toplam yığın büyümesine yol açabilir. . Yani alloca()olabilir destenizin için iyi dikkatlice kullanırsanız.

Bu teklifi buldum .... Tamam, bu teklifi yaptım. Ama gerçekten düşünün ....

@j_random_hacker diğer cevaplar altındaki yorumlarında çok haklı: alloca()Büyük boyutlu yerel diziler lehine kullanmaktan kaçınmak, programınızı yığın taşmalarından daha güvenli hale getirmez (derleyiciniz, alloca()hangi durumda kullanacağınız işlevlerin satır içine alınmasına izin verecek kadar büyük değilse) yükseltme veya alloca()iç döngüler kullanmadığınız sürece , bu durumda ... alloca()iç döngüler kullanmamalısınız ).

Masaüstü / sunucu ortamlarında ve gömülü sistemlerde çalıştım. Birçok gömülü sistem hiç yığın kullanmaz (destek için bağlantı bile oluşturmazlar), dinamik olarak ayrılan belleğin hiçbir zaman bir uygulamada bellek sızıntısı riski nedeniyle kötü olduğu algısını içeren nedenlerden dolayı bir seferde yıllarca yeniden başlatılır veya dinamik belleğin tehlikeli olduğu daha makul bir gerekçedir, çünkü bir uygulamanın yığınını hiçbir zaman sahte bellek tükenme noktasına parçalamayacağı kesin olarak bilinemez. Böylece gömülü programcılar birkaç alternatifle kalıyor.

alloca() (veya VLA'lar) iş için doğru araç olabilir.

Ben bir programcı "olası herhangi bir durumda işlemek için yeterince büyük" yığın tahsisli bir tampon yapar nerede tekrar zaman gördüm. Derin iç içe bir çağrı ağacında, bu (anti -?) Paternin tekrarlı kullanımı abartılı yığın kullanımına yol açar. (20 seviyeli bir çağrı ağacının, her düzeyde farklı nedenlerle, fonksiyonun genellikle sadece 16 veya daha azını kullanacağı ve yalnızca nadir durumlarda daha fazla kullanabilirsiniz.) Bir alternatif kullanmaktıralloca()veya VLA'lara dönüştürün ve yığının gereksiz yere yüklenmesini önlemek için yalnızca işlevinizin gerektirdiği kadar yığın alanı ayırın. Umarım çağrı ağacındaki bir işlev normalden daha büyük bir ayırmaya ihtiyaç duyduğunda, çağrı ağacındaki diğerleri normal küçük ayırmalarını kullanır ve genel uygulama yığını kullanımı her işlevin körü körüne yerel bir arabelleğe aşırı tahsis edilmesinden önemli ölçüde daha azdır. .

Ama kullanmayı seçerseniz alloca()...

Bu sayfadaki diğer cevaplara dayanarak, VLA'ların güvenli olması gerektiği görünüyor (bir döngüden çağrıldıklarında yığın tahsislerini birleştirmiyorlar), ancak kullanıyorsanız alloca(), bunu bir döngü içinde kullanmamaya dikkat edin ve başka bir işlevin döngüsü içinde çağırma olasılığı varsa işlevinizin satır içine alınamayacağından emin olun .


Bu noktaya katılıyorum. Tehlikesi alloca()doğrudur, ancak aynı şey bellek sızıntıları için de söylenebilir malloc()(neden bir GC kullanmıyorsunuz? alloca()dikkatle kullanıldığında yığın boyutunu azaltmak gerçekten yararlı olabilir.
Felipe Tonello

Özellikle gömülü olarak dinamik bellek kullanmamak için bir başka iyi neden: yığına yapışmaktan daha karmaşık. Dinamik bellek kullanmak özel prosedürler ve veri yapıları gerektirirken, yığın üzerinde (şeyleri basitleştirmek için) yığın işaretleyiciden daha yüksek bir sayı ekleme / çıkarma meselesidir.
tehftw

Sidenote: "Sabit bir arabellek kullanma [MAX_SIZE]" örneği, aşırı bellek belleğinin neden bu kadar iyi çalıştığını vurgular. Programlar, arabellek uzunluklarının sınırları dışında hiçbir zaman dokunamayacakları belleği ayırır. Bu yüzden Linux'un (ve diğer işletim sistemlerinin) ilk kez kullanılana kadar bir bellek sayfası atamaması iyi bir şeydir (hatalı olarak değil). Arabellek bir sayfadan büyükse, program yalnızca ilk sayfayı kullanabilir ve fiziksel belleğin geri kalanını boşa harcamaz.
Katastic Voyage

@KatasticVoyage MAX_SIZE, sisteminizin sanal bellek sayfası boyutundan büyük olmadığı (veya en azından eşit olduğu sürece), argümanınız su tutmaz. Ayrıca, sanal belleği olmayan gömülü sistemlerde (birçok gömülü MCU'nun MMU'su yoktur), aşırı komuta bellek politikası "programınızın her durumda çalışmasını sağlayın" bakış açısından iyi olabilir, ancak bu güvence yığın boyutunuzun fiyatı ile birlikte gelir aynı şekilde bu aşırı bellek ilkesini desteklemek için ayrılmalıdır. Bazı gömülü sistemlerde, bu düşük maliyetli ürün üreticilerinin ödemeye istekli olmadığı bir fiyattır.
phonetagger

11

Herkes zaten bir yığın taşması potansiyel tanımsız davranış olan büyük şey işaret etti, ancak Windows ortamının yapılandırılmış istisnalar (SEH) ve koruma sayfaları kullanarak bunu yakalamak için harika bir mekanizma olduğunu belirtmek gerekir. Yığın yalnızca gerektiği gibi büyüdüğünden, bu koruma sayfaları ayrılmamış alanlarda bulunur. Bunlara ayırırsanız (yığının üzerine taşarak) bir istisna atılır.

Bu SEH istisnasını yakalayabilir ve yığını sıfırlamak ve neşeli yolunuza devam etmek için _resetstkoflw'yi arayabilirsiniz. İdeal değil ama en azından bir şeyler fan vurduğunda bir şeyler yanlış olduğunu bilmek başka bir mekanizma. * nix'in farkında olmadığım bir şeyi olabilir.

Alloca'yı sararak ve dahili olarak izleyerek maksimum ayırma boyutunuzu kapatmanızı öneririm. Bu konuda gerçekten sert olsaydınız, fonksiyon kapsamındaki herhangi bir alloca tahsisini izlemek ve fonksiyonunuz için izin verilen maksimum miktara karşı bunu kontrol etmek için fonksiyonunuzun tepesine bazı kapsam nöbetçileri atabilirsiniz.

Ayrıca, bellek sızıntılarına izin vermemeye ek olarak, alloca oldukça önemli olan bellek parçalanmasına neden olmaz. Eğer akıllıca kullanırsanız, alloca'nın kötü bir uygulama olduğunu düşünmüyorum, bu temelde her şey için doğrudur. :-)


Sorun şu ki, o alloca()kadar çok alan gerektirebilir ki, yığın işaretçisi yığına iner. Bununla, boyutunu kontrol edebilen bir saldırgan alloca()ve bu arabelleğe giden veriler yığının üzerine yazabilir (ki bu çok kötüdür).
12431234123412341234123

SEH, yalnızca Windows'a yönelik bir şeydir. Kodunuzun yalnızca Windows'ta çalışmasını önemsiyorsanız, ancak kodunuzun platformlar arası olması gerekiyorsa (veya yalnızca Windows olmayan bir platformda çalışan kod yazıyorsanız) bu harika bir şeydir. SEH.
George

10

alloca () güzel ve etkili ... ama aynı zamanda derinden kırılmış.

  • bozuk kapsam davranışı (blok kapsamı yerine işlev kapsamı)
  • malloc ile tutarsız kullanın ( alloca () -pointer işaretçisi serbest bırakılmamalıdır, bu nedenle işaretçilerinizin nereden geldiğini izlemek zorundasınız () sadece malloc () ile olanları izlemek zorundasınız )
  • satır içi kullanıldığında da kötü davranış (kapsam bazen callee satır içi olup olmamasına bağlı olarak arayan işlevine gider).
  • yığın sınır kontrolü yok
  • başarısızlık durumunda tanımsız davranış (malloc gibi NULL döndürmez ... ve yine de yığın sınırlarını yine de kontrol etmediği için başarısızlık ne demektir ...)
  • ansi standardı değil

Çoğu durumda, yerel değişkenleri ve majorant boyutunu kullanarak değiştirebilirsiniz. Büyük nesneler için kullanılıyorsa, bunları yığına koymak genellikle daha güvenli bir fikirdir.

Gerçekten C ihtiyacınız varsa VLA kullanabilirsiniz (C ++ hiçbir vla, çok kötü). Kapsam davranışı ve tutarlılık açısından alloca () 'dan çok daha iyidirler. Gördüğüm gibi VLA bir çeşit doğru yapılmış alloca () .

Tabii ki, gerekli alanın bir majorantı kullanan yerel bir yapı veya dizi hala daha iyidir ve eğer böyle bir majorant yığın tahsisi yoksa düz malloc () kullanarak muhtemelen aklı başındadır. Gerçekten alloca () veya VLA'ya gerçekten ihtiyacınız olan aklı başında bir kullanım durumu görmüyorum .


Downvote nedenini görmüyorum (herhangi bir yorum olmadan, bu arada)
gd1

Yalnızca adların kapsamı vardır. allocabir isim oluşturmaz, sadece ömrü olan bir bellek aralığı oluşturur .
curiousguy

@curiousguy: sadece kelimelerle oynuyorsun. Otomatik değişkenler için, adın kapsamıyla eşleştiğinden, temel belleğin ömründen de söz edebilirim. Her neyse sorun buna nasıl dediğimiz değil, alloca tarafından döndürülen yaşam boyu / bellek kapsamının dengesizliği ve istisnai davranış.
kriss

2
Keşke alloca karşılık gelen bir "freea", "freea" çağrısı nesne ve sonraki tüm olanlar oluşturan "alloca" etkilerini geri alacak bir belirtim vardı ve bir fucntion içinde depolama 'alloca'ed bir gereklilik içinde 'özgürce' olun. Bu, neredeyse tüm uygulamaların alloka / freea'yı uyumlu bir şekilde desteklemesini mümkün kılacak, satır içi sorunları hafifletecek ve genellikle işleri daha temiz hale getirecektir.
supercat

2
@supercat - Ben de isterim. Bu nedenle (ve daha fazla) için, ben şimdiye kadar demiyorlar böylece bir soyutlama katmanı (çoğunlukla makro ve satır içi fonksiyonlar) kullanmak allocaveya mallocveya freedoğrudan. Olayların gibi söylemek {stack|heap}_alloc_{bytes,items,struct,varstruct}ve {stack|heap}_dealloc. Yani, heap_deallocsadece arar freeve stack_deallocbir hayır-op. Bu şekilde, yığın ayırmaları yığın ayırmalarına kolayca düşürülebilir ve niyetler de daha açıktır.
Todd Lehman

9

İşte nedeni:

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

Kimse bu kodu yazacaktı değil, ama geçtiğiniz boyut argümanı allocaneredeyse kesinlikle bir tür girdiden geliyor, bu da programınızı allocaböyle büyük bir şeye kötü amaçlı bir şekilde getirmeyi amaçlayabilir . Sonuçta, boyut girdiye dayanmıyorsa veya büyük olma olasılığı yoksa, neden sadece küçük, sabit boyutlu bir yerel tampon bildirmediniz?

Hemen hemen tüm kodlar allocave / veya C99 vlas kullanarak ciddi hatalar vardır, bu da çökmelere (şanslıysanız) veya ayrıcalık uzlaşmasına (çok şanslı değilseniz) neden olacak ciddi hatalara sahiptir.


1
Dünya asla bilemeyebilir. :( Bu, hakkında bir soru açıklığa kavuşturabileceğinizi umuyorum alloca. Bunu kullanan neredeyse tüm kodların bir hata olduğunu söylediniz, ama kullanmayı planlıyordum; Normalde böyle bir iddiayı görmezden gelirdim, ama geliyor Sanal bir makine yazıyorum ve devasa hızlanma nedeniyle yığın yerine işlevden kaçmayan değişkenleri devingen olarak ayırmak istiyorum. Alternatif var mı? aynı performans özelliklerine sahip bir yaklaşım mı? Bellek havuzlarına yaklaşabileceğimi biliyorum, ama yine de ucuz değil. Ne yapardınız?
GManNickG

7
Neyin tehlikeli olduğunu da biliyor musun? Bu: *0 = 9;İNANILMAZ !!! Asla işaretçiler kullanmamalıyım (ya da en azından onlardan vazgeçme). Hata. Boş olup olmadığını test edebilirim. Hmm. Ayrılmak istediğim belleğin boyutunu da test edebilirim sanırımalloca . Tuhaf adam. Tuhaf.
Thomas Eding

7
*0=9; Geçerli değil C. Geçtiğiniz boyutu test etmek için alloca test etmek , neye karşı test edin? Sınırı bilmenin bir yolu yoktur ve eğer küçük sabit bilinen güvenli bir boyuta (örneğin 8k) karşı test edecekseniz, yığın üzerinde sabit boyutlu bir dizi de kullanabilirsiniz.
R .. GitHub BUZA YARDIMCI DURDUR

7
Gördüğünüz gibi "ya büyüklüğünün yeterince küçük olduğu ya da girdiye bağımlı olduğu ve bu nedenle keyfi olarak büyük olabileceği" iddiasıyla ilgili sorun, özyineleme için de aynı şekilde geçerli olduğudur. Pratik bir uzlaşma (her iki durum için), boyutunsmall_constant * log(user_input) muhtemelen yeterli belleğe sahip olduğumuzu .
j_random_hacker

1
Gerçekten de, VLA / alloca'nın yararlı olduğu ONE vakasını tanımladınız: herhangi bir çağrı çerçevesinde ihtiyaç duyulan maksimum alanın N kadar büyük olabileceği, ancak tüm özyineleme seviyelerinde gereken alanın toplamının N veya bir fonksiyon olduğu yinelemeli algoritmalar hızlı büyümeyen N'nin.
R .. GitHub BUZA YARDIMCI DURDUR

9

Kimsenin bundan bahsettiğini sanmıyorum: Bir fonksiyonda alloca kullanımı, derleyici fonksiyonun yığın çerçevesinin boyutunu bilemeyeceği için fonksiyonda uygulanabilecek bazı optimizasyonları engelleyecek veya devre dışı bırakacaktır.

Örneğin, C derleyicileri tarafından ortak bir optimizasyon, çerçeve işaretçisinin bir işlev içinde kullanımını ortadan kaldırmaktır, bunun yerine çerçeve erişimleri yığın işaretçisine göre yapılır; yani genel kullanım için bir kayıt daha var. Ancak alloca fonksiyon içinde çağrılırsa, fonksiyonun bir kısmı için sp ve fp arasındaki fark bilinmeyecektir, bu nedenle bu optimizasyon yapılamaz.

Kullanımı nadirliğini ve standart bir fonksiyon olarak gölgeli statüsünü, derleyici tasarımcıları büyük ihtimalle devre dışı Verilen herhangi optimizasyonu o kudreti , alloca ile çalışmak için biraz çaba harcayacaksa, alloca ile ilgili sorunlara neden .

GÜNCELLEME: Değişken uzunlukta yerel diziler C'ye eklendiğinden ve bunlar derleyiciye alloca olarak çok benzer kod oluşturma sorunları sunduğundan, 'kullanım nadirliği ve gölgeli durum'un altta yatan mekanizma için geçerli olmadığını görüyorum; ancak yine de alloca veya VLA kullanımının, bunları kullanan bir işlev içinde kod üretiminden ödün verme eğiliminde olduğundan şüphelenirim. Derleyici tasarımcılarından gelen geri bildirimleri memnuniyetle karşılarım.


1
Değişken uzunluklu diziler hiçbir zaman C ++ 'a eklenmedi.
Nir Friedman

@NirFriedman Gerçekten. Eski bir teklife dayanan bir wikipedia özellik listesi olduğunu düşünüyorum.
19:03 greggo

> Yine de alloca veya VLA kullanımının kod üretiminden ödün verme eğiliminde olduğundan şüphelenirim . Alloca kullanımının bir çerçeve işaretçisi gerektirdiğini düşünürüm, çünkü yığın işaretçisi derleme zamanında belirgin olmayan şekilde hareket eder. alloca daha fazla yığın bellek veya çalışma zamanı hesaplanmış boyut, vb kapmak için bir döngüde çağrılabilir. Bir çerçeve işaretçisi varsa, oluşturulan kod yerliler için istikrarlı bir başvuru vardır ve yığın işaretçi istediği her şeyi yapabilir; kullanılmaz.
Kaz

8

İle bir tuzak alloca, onu longjmpgeri sarar.

Başka bir deyişle, bir bağlamı setjmp, ardından allocabir hafızayı, daha sonra longjmpbağlama kaydederseniz, allocahafızayı kaybedebilirsiniz . Yığın işaretçisi bulunduğu yere geri döndü ve böylece bellek artık ayrılmıyor; bir işlevi çağırır veya başka bir işlev yaparsanız alloca, orijinali tıkayacaksınız alloca.

Açıkça söylemek gerekirse, burada özellikle bahsettiğim şey longjmp, gerçekleşen işlevden geri dönmeyen bir durum alloca! Aksine, bir işlev bağlamı setjmp; daha sonra ile bellek ayırır allocave son olarak bu bağlamda bir longjmp yer alır. Bu işlevin allocahafızası tamamen serbest değil; sadece o andan beri ayırdığı bellek setjmp. Tabii ki, gözlemlenen bir davranıştan bahsediyorum; allocabildiğim herhangi bir şey için böyle bir gereklilik belgelenmemiştir .

Belgelerdeki odak noktası genellikle allocabelleğin herhangi bir blokla değil, bir fonksiyon aktivasyonu ile ilişkili olduğu konseptindedir ; birden fazla çağırma allocaişlevi, işlev sonlandığında serbest bırakılan daha fazla yığın bellek alır. Öyle değil; bellek aslında prosedür bağlamıyla ilişkilidir. Bağlam geri yüklendiğinde longjmp, önceki allocadurum da geri yüklenir . Bu, yığın işaretçisi kaydının kendisinin tahsis için kullanılmasının bir sonucudur ve ayrıca (mutlaka) içinde kaydedilip geri yüklenir jmp_buf.

Bu arada, bu, eğer bu şekilde çalışırsa, tahsis edilen hafızayı kasıtlı olarak boşaltmak için makul bir mekanizma sağlar alloca.

Ben bir hata kök nedeni olarak bu var.


1
Gerçi yapması gereken şey budur - longjmpgeri döner ve program yığında gerçekleşen her şeyi unutur: tüm değişkenler, fonksiyon çağrıları vb. Ve allocatıpkı yığındaki bir dizi gibidir, bu yüzden hızlandırılmaları beklenir yığıntaki diğer her şey gibi.
tehftw

1
man allocaşu cümleyi verdi: "alloca () tarafından ayrılan alan yığın çerçevesi içinde tahsis edildiğinden, işlev dönüşü longjmp (3) veya siglongjmp (3) çağrısı ile atlanırsa bu alan otomatik olarak serbest bırakılır." Bu nedenle, ile ayrılan belleğin allocaa longjmp.
tehftw

@tehftw Açıklanan durum, işlev dönüşü atlanmadan gerçekleşir longjmp. Hedef işlev henüz geri dönmedi. Bu çıkarmış setjmp, allocasonra ve longjmp. longjmpGeri sarmak olabilir allocao ucunda ne devlet geri setjmpzamanında. Yani, hareket ettirilen işaretçi allocaişaretlenmemiş bir yerel değişkenle aynı problemden muzdariptir volatile!
Kaz

3
Neden beklenmedik olması gerektiğini anlamıyorum. O setjmpzaman alloca, ve sonra longjmp, allocageri sarılması normaldir. Bütün mesele longjmp, kurtarılan duruma geri dönmek setjmp!
tehftw

@tehftw Bu özel etkileşimin daha önce belgelenmediğini gördüm. Bu nedenle, derleyicilerle yapılan ampirik soruşturma dışında her iki şekilde de güvenilemez.
Kaz

7

Tipik bir işletim sisteminin çekirdeğinden alloca()özellikle tehlikeli olan bir yerde malloc(), başlığından birine sabit olarak kodlanmış sabit boyutlu bir yığın alanı vardır; bir uygulamanın yığını kadar esnek değildir. alloca()Yanlış boyutta bir arama yapmak çekirdeğin çökmesine neden olabilir. Bazı derleyiciler alloca()bir çekirdek kodu derlenirken açılması gereken belirli seçenekler altında kullanımını (ve hatta bu konuda VLA'ları) uyarır - burada, bellekte sabit kodlanmış bir sınırla sabitlenmemiş bellek ayırmak daha iyidir.


7
alloca()keyfi bir tamsayı int foo[bar];nerede barolduğundan daha tehlikeli değildir .
Todd Lehman

@ToddLehman Bu doğru ve bu nedenle çekirdekte VLA'ları birkaç yıldır yasakladık ve 2018'den beri VLA'sız olduk :-)
Chris Down

6

Yanlışlıkla alloca(örneğin bir arabellek taşması nedeniyle) ayrılan bloğun ötesine yazarsanız, işlevinizin dönüş adresinin üzerine yazarsınız , çünkü bu, yığının üzerinde "yukarıda", yani ayrılmış bloğunuzdan sonra bulunur .

Yığında _alloca bloğu

Bunun sonucu iki yönlüdür:

  1. Program muhteşem bir şekilde çökecek ve neden veya nerede çöktüğünü söylemek imkansız olacaktır (yığın büyük olasılıkla üzerine yazılan çerçeve işaretçisi nedeniyle rastgele bir adrese çözülecektir).

  2. Kötü niyetli bir kullanıcı yığına konacak özel bir yük oluşturabildiğinden ve sonuç olarak yürütülebildiğinden, arabellek taşmasını çok daha tehlikeli hale getirir.

Buna karşılık, öbek üzerinde bir blok ötesinde yazarsanız yığın yolsuzluk "sadece" olsun. Program muhtemelen beklenmedik bir şekilde sonlandırılacak, ancak yığını düzgün bir şekilde çözecek ve böylece kötü amaçlı kod yürütme şansını azaltacaktır.


11
Bu durumda hiçbir şey, tamponun taşan sabit boyutlu yığın tahsisli bir tamponun tehlikelerinden önemli ölçüde farklı değildir. Bu tehlike benzersiz değildir alloca.
phonetagger

2
Tabii ki değil. Ama lütfen orijinal soruyu kontrol edin. Soru şudur: ile allocakarşılaştırıldığında tehlikesi nedir malloc(böylece yığın üzerinde sabit boyutlu tampon değildir).
rustyx

Küçük nokta, ancak bazı sistemlerde yığınlar yukarı doğru büyür (örn. PIC 16 bit mikroişlemciler).
EBlake

5

Ne yazık ki gerçekten harika alloca()neredeyse harika tcc eksik. Gcc var alloca().

  1. Kendi imha tohumunu eker. Yıkıcı olarak dönüş ile.

  2. malloc()Bir MMU ile modern sistemlerde segfault olacak başarısız bir geçersiz işaretçi döndürür gibi (ve umarım olmadan olanlar yeniden başlatın).

  3. Otomatik değişkenlerin aksine, boyutu çalışma zamanında belirtebilirsiniz.

Özyineleme ile iyi çalışır. Kuyruk özyinelemesine benzer bir şey elde etmek için statik değişkenleri kullanabilir ve her yinelemeye yalnızca birkaç bilgi iletmek için kullanabilirsiniz.

Çok derine basarsanız, bir segfault olduğundan emin olursunuz (MMU'nuz varsa).

Not o malloc()teklifler artık sistem belleği yetersiz olduğunda (eğer atanmışsa da segfault olacaktır) NULL döndürüyor olarak. Yani yapabileceğiniz tek şey kefalet veya sadece herhangi bir şekilde atamaya çalışın.

Kullanmak için malloc()globalleri kullanıyorum ve NULL atarım. İşaretçi BOŞ değilse, kullanmadan önce serbest bırakırım malloc().

realloc()Mevcut verileri kopyalamak istiyorsanız genel durum olarak da kullanabilirsiniz . Eğer sonra kopyalamak veya bitiştirmek için çalışacak önce işaretçisi kontrol etmek gerekir realloc().

3.2.5.2 Aloka'nın avantajları


4
Aslında alloca spec, başarısız bir geçersiz işaretçi döndürdüğünü söylemez (yığın taşması) tanımsız bir davranışa sahip olduğunu söyler ... ve malloc için rastgele bir geçersiz işaretçi değil NULL döndürdüğünü söyler (Tamam, Linux iyimser bellek uygulaması bunu yapar Faydasız).
kriss

@kriss Linux sürecinizi öldürebilir, ancak en azından tanımlanmamış davranışlara
girmez

@ craig65535: tanımsız davranış ifadesi genellikle bu davranışın C veya C ++ belirtimi ile tanımlanmadığı anlamına gelir. Herhangi bir işletim sisteminde veya derleyicide rastgele veya kararsız olacağı hiçbir şekilde değil. Bu nedenle UB'yi "Linux" veya "Windows" gibi bir işletim sistemi adıyla ilişkilendirmek anlamsızdır. Onunla hiçbir ilgisi yok.
kriss

Malloc'un NULL döndürdüğünü veya Linux durumunda, işleminizi öldüren bir bellek erişiminin, tanımlanmamış alloca davranışına tercih edildiğini söylemeye çalışıyordum. Sanırım ilk yorumunu yanlış okumalıydım.
craig65535

3

İşlemlerin yalnızca sınırlı miktarda yığın alanı vardır - kullanılabilir bellek miktarından çok daha az malloc().

Kullanarak alloca()bir Yığın Taşması hatası alma şansınızı önemli ölçüde artırın (eğer şanslıysanız ya da değilseniz açıklanamaz bir çökme).


Bu, uygulamaya çok bağlıdır. Bellek sınırlı bir gömülü uygulamanın yığın boyutundan daha büyük bir yığın boyutuna sahip olması olağandışı değildir (bir yığın bile olsa).
EBlake

3

Çok hoş değil, ama performans gerçekten önemliyse, yığın üzerinde biraz yer ayırabilirsiniz.

Zaten ihtiyacınız olan bellek bloğunun maksimum boyutunu zaten taşıyorsanız ve taşma kontrollerini sürdürmek istiyorsanız, şöyle bir şey yapabilirsiniz:

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}

12
Char dizisinin herhangi bir veri türü için doğru hizalanması garanti ediliyor mu? alloca böyle bir vaatte bulunur.
Juho Östman

@ JuhoÖstman: hizalama sorunlarınız varsa char yerine bir yapı dizisi (veya her türden) kullanabilirsiniz.
kriss

Buna Değişken Uzunluk Dizisi denir . C90 ve üstü sürümlerde desteklenir, ancak C ++ ile desteklenmez. Bkz . C ++ 03 ve C ++ 11'de bir C Değişken Uzunluk Dizisi kullanabilir miyim?
jww

3

Aloka fonksiyonu harika ve tüm muhalifler sadece FUD yayıyorlar.

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

Dizi ve parray tam olarak aynı risklerle tam olarak aynıdır. Birinin diğerinden daha iyi olduğunu söylemek, teknik değil, sözdizimsel bir seçimdir.

Yığın değişkenlerine karşı yığın değişkenlerini seçmeye gelince, kapsam içi yaşam süreleri olan değişkenler için yığın üzerinden yığın kullanan uzun çalışan programlara bir çok avantaj vardır. Öbek parçalanmasını önler ve işlem alanınızı kullanılmayan (kullanılamaz) yığın alanı ile büyütmekten kaçınabilirsiniz. Temizlemenize gerek yok. İşlemdeki yığın tahsisini kontrol edebilirsiniz.

Bu neden kötü?


3

Aslında, alloca'nın yığını kullanacağı garanti edilmez. Gerçekten, alloka'nın gcc-2.95 uygulaması, malloc'un kendisini kullanarak öbekten bellek ayırır. Ayrıca bu uygulama hatalıdır, daha fazla goto kullanımı ile bir blok içinde çağırırsanız bellek sızıntısına ve beklenmedik davranışlara yol açabilir. Hiçbir zaman kullanmamanız gerektiğini söylemek için değil, ama bazen alloca, kurbağayı serbest bıraktığından daha fazla yüke yol açar.


Gcc-2.95 alloca kırdı gibi görünüyor ve muhtemelen gerektiren programlar için güvenli bir şekilde kullanılamaz alloca. longjmpGerçekleştirilen kareleri terk etmek için kullanıldığında belleği nasıl temizlerdi alloca? Bugün gcc 2.95 ne zaman birileri kullanılır?
Kaz

2

IMHO, alloca kötü uygulama olarak kabul edilir, çünkü herkes yığın boyutu sınırını tüketmekten korkar.

Bu konuyu ve diğer bazı bağlantıları okuyarak çok şey öğrendim:

Alloca'yı esas olarak düz C dosyalarımı msvc ve gcc'de herhangi bir değişiklik, C89 stili, #ifdef _MSC_VER vb.

Teşekkür ederim ! Bu konu beni bu siteye kaydettirdi :)


Bu sitede "iş parçacığı" diye bir şey olmadığını unutmayın. Yığın Taşması bir tartışma iş parçacığı biçimine değil, bir soru-cevap biçimine sahiptir. "Yanıt" bir tartışma forumundaki "Yanıtla" gibi değildir; aslında soruya bir cevap verdiğiniz anlamına gelir ve diğer cevaplara cevap vermek veya konu hakkında yorum yapmak için kullanılmamalıdır. En az 50 temsilciniz olduğunda yorum gönderebilirsiniz , ancak "Ne zaman yorum yapmamalıyım ?" Bölüm. Sitenin biçimini daha iyi anlamak için lütfen Hakkında sayfasını okuyun .
Adi Inbar

1

Bence alloca (), sadece kısıtlı bir şekilde kullanılmalıdır. Çok "goto" kullanımı gibi, aksi takdirde makul sayıda çok sayıda insan sadece alloca () kullanımı değil, aynı zamanda varlığı için güçlü bir isteksizlik vardır.

Yığın boyutunun bilindiği ve ayırma boyutunun konvansiyonu ve analizi ile limitlerin uygulanabildiği ve derleyicinin C99 + 'yı destekleyecek şekilde yükseltilemediği gömülü kullanım için, alloca () kullanımı iyidir ve ben kullanmak için bilinir.

Var olduğunda, VLA'ların alloca () üzerinde bazı avantajları olabilir: Derleyici, dizi stili erişimi kullanıldığında sınırların dışındaki erişimi yakalayacak yığın sınırı kontrolleri oluşturabilir (herhangi bir derleyicinin bunu yapıp yapmadığını bilmiyorum, ancak kodun analizi dizi erişim ifadelerinin düzgün bir şekilde bağlı olup olmadığını belirleyebilir. Otomotiv, tıbbi ekipman ve aviyonik gibi bazı programlama ortamlarında bu analizin, hem otomatik (yığın üzerinde) hem de statik ayırma (global veya yerel) gibi sabit boyutlu diziler için bile yapılması gerektiğini unutmayın.

Yığın üzerinde hem verileri hem de dönüş adreslerini / çerçeve işaretleyicilerini depolayan mimarilerde (bildiklerimden, hepsi budur), değişkenin adresi alınabileceğinden ve denetlenmeyen giriş değerlerine izin verebileceğinden, herhangi bir yığın tahsis edilen değişken tehlikeli olabilir her türlü yaramazlık.

Taşınabilirlik, gömülü alanda daha az endişe kaynağı olmakla birlikte, dikkatle kontrol edilen koşullar dışında alloca () kullanımına karşı iyi bir argümandır.

Gömülü alanın dışında, verimlilik için çoğunlukla günlük kaydı ve biçimlendirme işlevlerinin içinde alloca () kullandım ve tokenizasyon ve sınıflandırma sırasında geçici yapıların (alloca () kullanılarak ayrılmış, daha sonra kalıcı) oluşturulduğu yinelemesiz bir sözlük tarayıcıda kullandım. fonksiyon (malloc () yoluyla tahsis edilir) fonksiyon geri dönmeden önce doldurulur.Daha küçük geçici yapılar için alloca () kullanımı kalıcı nesne tahsis edildiğinde parçalanmayı büyük ölçüde azaltır.


1

Buradaki yanıtların çoğu büyük ölçüde noktayı kaçırıyor: _alloca() , yalnızca büyük nesneleri yığınta depolamaktan daha kötü .

Otomatik depolama arasındaki temel fark _alloca(), ikincisinin ek (ciddi) bir sorundan muzdarip olmasıdır: tahsis edilen blok derleyici tarafından kontrol edilmez , bu nedenle derleyicinin onu optimize etmesine veya geri dönüştürmesine imkan yoktur.

Karşılaştırmak:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

ile:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

İkincisi ile ilgili sorun açık olmalıdır.


VLA ve alloca(evet, VLA diyorum, çünkü allocasadece statik boyutlu dizilerin yaratıcısından daha fazlası) arasındaki farkı gösteren pratik örnekleriniz var mı?
Ruslan

Birincisinin desteklemediği ikincisi için kullanım örnekleri vardır. Döngü 'n' kez çalıştıktan sonra - belki bir bağlantılı liste veya ağaçta 'n' kayıtları olmasını isteyebilirsiniz; bu veri yapısı, fonksiyonun sonunda geri döndüğü zaman atılır. Bu bir şey bu şekilde kodlamak demek değildir :-)
greggo

1
"Derleyici bunu kontrol edemez" diyebilirim, çünkü alloca () bu şekilde tanımlanır; modern derleyiciler alloka'nın ne olduğunu bilir ve ona özel davranır; 80'lerde olduğu gibi sadece bir kütüphane işlevi değil. C99 VLA'lar temel olarak blok kapsamına sahip allokalardır (ve daha iyi yazım). Daha fazla veya daha az kontrol değil, sadece farklı semantiğe uygun.
greggo

@greggo: Eğer downvoter iseniz, cevabımın neden faydalı olmadığını düşündüğünüzü memnuniyetle duyarım.
alecov

C'de geri dönüşüm derleyicinin görevi değildir, bunun yerine c kütüphanesinin görevidir (free ()). alloca () dönüşte serbest bırakılır.
peterh - Monica'yı

1

Kimsenin bundan bahsetmediğini düşünmüyorum, ancak alloca'nın malloc ile zorunlu olarak bulunmayan bazı ciddi güvenlik sorunları da var (bu konular dinamik veya değil, yığın tabanlı dizilerde de ortaya çıkıyor). Bellek yığına tahsis edildiğinden, arabellek taşmaları / taşmaları sadece malloc'dan çok daha ciddi sonuçlara yol açar.

Özellikle, bir işlevin dönüş adresi yığında depolanır. Bu değer bozulursa, kodunuz belleğin yürütülebilir herhangi bir bölgesine gitmek için oluşturulabilir. Derleyiciler bunu zorlaştırmak için büyük çaba harcıyorlar (özellikle adres yerleşimini rastgele seçerek). Bununla birlikte, bu, bir yığın taşmasından daha kötüdür, çünkü en iyi durum, dönüş değeri bozulursa bir SEGFAULT'dur, ancak rastgele bir bellek parçası veya en kötü durumda programınızın güvenliğini tehlikeye atan bazı bellek bölgelerini yürütmeye başlayabilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.