Yığın gevşemesi nedir?


193

Yığın gevşemesi nedir? Arandı, ancak aydınlatıcı bir cevap bulamadık!


76
Ne olduğunu bilmiyorsa, C ve C ++ için aynı olmadıklarını nasıl bilmesini bekleyebilirsiniz?
dreamlax

@dreamlax: Peki, "yığın gevşetme" kavramı C & C ++ 'da nasıl farklı?
Destructor

2
@PravasiMeet: C'nin bir istisna yönetimi yoktur, bu nedenle yığın çözme çok düzdür, ancak C ++ 'da bir istisna atılırsa veya bir işlev çıkarsa, yığın çözme otomatik depolama süresine sahip herhangi bir C ++ nesnesinin yok edilmesini içerir.
dreamlax

Yanıtlar:


150

Yığın gevşemesi genellikle istisna işleme ile bağlantılı olarak konuşulur. İşte bir örnek:

void func( int x )
{
    char* pleak = new char[1024]; // might be lost => memory leak
    std::string s( "hello world" ); // will be properly destructed

    if ( x ) throw std::runtime_error( "boom" );

    delete [] pleak; // will only get here if x == 0. if x!=0, throw exception
}

int main()
{
    try
    {
        func( 10 );
    }
    catch ( const std::exception& e )
    {
        return 1;
    }

    return 0;
}

Burada pleakbir istisna atılırsa ayrılan bellek kaybolur, buna ayrılan bellek her durumda yıkıcı starafından düzgün bir şekilde serbest bırakılır std::string. Kapsamdan çıkıldığında yığına ayrılan nesneler "çözülür" (burada kapsam fonksiyonun içindedir func.) Bu, derleyici otomatik (yığın) değişkenlerin yıkıcılarına çağrı ekleyerek yapılır.

Şimdi bu, C ++ 'da bellek, veritabanı bağlantıları, açık dosya tanımlayıcıları, vb. Gibi kaynakları yönetmemize yardımcı olan RAII adı verilen , yani Kaynak Alımı Başlatma tekniğine giden çok güçlü bir kavramdır .

Şimdi bu, istisna güvenlik garantileri sağlamamıza izin veriyor .


Bu gerçekten aydınlatıcıydı! Bu yüzden bunu alıyorum: işlemim yığın yığını patlatıldığı HERHANGİ BİR blok ayrılırken beklenmedik bir şekilde çökerse, istisna işleyici kodundan sonra kodun yürütülmeyeceği ve bellek sızıntılarına neden olabileceği, yığın bozulması vb.
Rajendra Uppal

15
Program "çökerse" (yani bir hata nedeniyle sona ererse ), bellek sonlandığında serbest bırakıldığından, bellek sızıntısı veya yığın bozulması önemsizdir.
Tyler McHenry

1
Kesinlikle. Teşekkürler. Bugün sadece biraz disleksik oluyorum.
Nikolai Fetissov

11
@TylerMcHenry: Standart, kaynakların veya belleğin sonlandırma sırasında serbest bırakıldığını garanti etmez. Ancak çoğu işletim sistemi bunu yapıyor.
Mooing Duck

3
delete [] pleak;yalnızca x == 0 ise ulaşılır.
Jib

71

Bütün bunlar C ++ ile ilgilidir:

Tanım : Nesneleri statik olarak oluştururken (yığın belleğinde yığın belleğine ayırmak yerine) ve işlev çağrıları gerçekleştirdikçe "yığılır".

Bir kapsamdan ( {ve sınırlandırılmış herhangi bir şey }) çıkıldığında ( return XXX;kapsamın sonuna ulaşarak veya bir istisna atarak) bu kapsamdaki her şey yok edilir (her şey için yıkıcılar çağrılır). Yerel nesneleri yok etme ve yıkıcıları çağırma işlemine yığın çözme denir.

Yığın çözme ile ilgili aşağıdaki sorunlarınız var:

  1. bellek sızıntılarından kaçınmak (dinamik olarak ayrılan ve yerel bir nesne tarafından yönetilmeyen ve yıkıcıda temizlenen herhangi bir şey sızdırılır) - Nikolai tarafından atıfta bulunulan RAII'ye ve boost :: scoped_ptr veya boost :: mutex kullanma örneğine bakın :: scoped_lock .

  2. program tutarlılığı: C ++ belirtimleri, varolan herhangi bir özel durum işlenmeden önce hiçbir zaman özel durum atmamanız gerektiğini belirtir. Bu , yığın çözme işleminin hiçbir zaman istisna atmaması gerektiği anlamına gelir (ya sadece yıkıcılara atmama garantili kodu kullanın ya da try {ve ile yıkıcılardaki her şeyi çevreleyin } catch(...) {}).

Herhangi bir yıkıcı yığın gevşetme sırasında bir istisna atarsa , programınızın beklenmedik bir şekilde (en yaygın davranış) veya evrenin sona ermesine (teorik olarak mümkün ancak henüz uygulamada gözlemlenmemiş) sonlanmasına neden olabilecek tanımlanmamış davranış ülkesine girersiniz .


2
Aksine. Gotos kötüye kullanılmamalıdır, ancak MSVC'de yığın gevşemesine neden olurlar (GCC'de değil, bu yüzden muhtemelen bir uzantıdır). setjmp ve longjmp bunu daha az esneklikle çapraz platformda yapar.
Patrick Niedzielski

10
Ben sadece bunu gcc ile test ettim ve bir kod bloğundan çıktığınızda doğru yıkıcıları çağırıyor. Bkz. Stackoverflow.com/questions/334780/… - bu bağlantıda belirtildiği gibi, bu standardın bir parçasıdır.
Damyan

1
Nikolai'nin, jrista'nın ve cevabınızı bu sırayla okumak, şimdi mantıklı!
n611x007

@sashoalm Yedi yıl sonra bir yayını düzenlemenin gerçekten gerekli olduğunu düşünüyor musunuz?
David Hoelzer

41

Genel anlamda, bir "gevşeme" yığını, bir işlev çağrısının sonu ve yığının sonraki patlamasıyla hemen hemen eş anlamlıdır.

Ancak, özellikle C ++ durumunda, yığın çözme, C ++ 'ın herhangi bir kod bloğunun başlamasından bu yana tahsis edilen nesneler için yıkıcıları nasıl çağırdığıyla ilgilidir. Blok içinde oluşturulan nesneler, ayırma işlemlerinin tersi sırada yeniden konumlandırılır.


4
tryBloklar hakkında özel bir şey yok . Herhangi bir blokta (isteseniz de tryolmasın) ayrılan yığın nesneleri , blok çıktığında gevşeyebilir.
Chris Jester-Young

Ben çok C ++ kodlama yaptım beri bir süredir. Bu cevabı paslı derinliklerinden kazmak zorunda kaldım. ; P
jrista

endişelenme. Herkesin ara sıra "kötüleri" vardır.
bitc

13

Yığın çözme, çoğunlukla C ++ konseptidir ve yığıntan ayrılmış nesnelerin kapsamından çıkıldığında (normal olarak veya bir istisna yoluyla) nasıl yok edildiğiyle ilgilenir.

Bu kod parçasına sahip olduğunuzu varsayalım:

void hw() {
    string hello("Hello, ");
    string world("world!\n");
    cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"

Bu herhangi bir blok için geçerli mi? Yani sadece {// bazı yerel nesneler} varsa
Rajendra Uppal

@Rajendra: Evet, anonim bir blok kapsam alanını tanımlar, bu yüzden de önemlidir.
Michael Myers

12

Henüz okuyup okumadığınızı bilmiyorum ama Wikipedia'nın çağrı yığınıyla ilgili makalesi iyi bir açıklamaya sahip.

gevşemeden:

Aranan işlevden döndüğünüzde, üst kareyi yığının dışına çıkarabilir, belki de bir dönüş değeri bırakabilirsiniz. Programın başka bir yerinde yürütmeyi sürdürmek için bir veya daha fazla kareyi yığının dışına atmanın daha genel eylemi denir yığın çözme ve özel durum işleme için kullanılanlar gibi yerel olmayan kontrol yapıları kullanıldığında gerçekleştirilmelidir. Bu durumda, bir işlevin yığın çerçevesi özel durum işleyicilerini belirten bir veya daha fazla girdi içerir. Bir istisna atıldığında, atılan istisnanın türünü işlemek (yakalamak) için hazırlanmış bir işleyici bulunana kadar yığın açılır.

Bazı diller genel gevşemeyi gerektiren başka kontrol yapılarına sahiptir. Pascal, genel goto deyiminin, iç içe geçmiş bir işlevden ve önceden çağrılan bir dış işleve aktarılmasını sağlar. Bu işlem yığının açılmasını gerektirir ve kontrolü ekteki dış işlev içindeki hedef ifadeye aktarmak üzere uygun bağlamı geri yüklemek için gerektiği kadar yığın çerçevesini kaldırır. Benzer şekilde, C, lokal olmayan gotolar olarak işlev gören setjmp ve longjmp işlevlerine sahiptir. Common Lisp, çözme koruması özel operatörünü kullanarak yığın çözüldüğünde ne olacağını kontrol etmenizi sağlar.

Bir devam uygularken, yığın (mantıksal olarak) açılır ve daha sonra devam yığını ile geri sarılır. Sürekliliği uygulamanın tek yolu bu değildir; örneğin, birden çok açık yığın kullanarak, bir devamın uygulanması yığınını etkinleştirebilir ve geçirilecek bir değeri sarabilir. Şema programlama dili, bir devam çağrıldığında kontrol yığınının "gevşetilmesi" veya "geri sarılması" üzerinde belirtilen noktalarda keyfi seslerin yürütülmesine izin verir.

Muayene [değiştir]


9

Anlamama yardımcı olan bir blog yazısı okudum.

Yığın gevşemesi nedir?

Özyinelemeli işlevleri destekleyen herhangi bir dilde (yani Fortran 77 ve Brainf * ck hariç hemen hemen her şey), dil çalışma zamanı şu anda hangi işlevlerin yürütülmekte olduğunu bir yığın halinde tutar. Yığın çözme, bu yığını incelemenin ve muhtemelen değiştirmenin bir yoludur.

Bunu neden yapmak istiyorsun?

Cevap açık görünebilir, ancak gevşemenin yararlı veya gerekli olduğu birkaç ilişkili, ancak farklı derecede farklı durumlar vardır:

  1. Çalışma zamanı kontrol akış mekanizması olarak (C ++ istisnaları, C longjmp (), vb.).
  2. Hata ayıklayıcıda, kullanıcıya yığını göstermek için.
  3. Bir profil oluşturucuda, yığının bir örneğini almak.
  4. Programın kendisinden (yığını göstermek için bir kilitlenme işleyicisinden olduğu gibi).

Bunların çok farklı gereksinimleri vardır. Bunlardan bazıları performans açısından kritik, bazıları değil. Bazıları dış çerçeveden kayıtları yeniden oluşturma yeteneğini gerektirir, bazıları gerektirmez. Ama bir saniyede bütün bunlara gireceğiz.

Yayının tamamını burada bulabilirsiniz .


7

Herkes C ++ 'da istisna işleme hakkında konuştu. Ancak, yığın gevşemesi için başka bir çağrışım olduğunu düşünüyorum ve bu hata ayıklama ile ilgili. Bir hata ayıklayıcı, geçerli kareden önceki bir kareye gitmesi gerektiğinde yığın çözme işlemini yapmak zorundadır. Ancak bu, mevcut kareye geri döndüğünde geri sarılması gerektiği için bir çeşit sanal gevşemedir. Bunun örneği gdb'de yukarı / aşağı / bt komutları olabilir.


5
Hata ayıklayıcı eylemine, genellikle yığını yığınlayan "Stack Walking" denir. "Stack Unwinding" sadece "Stack Walking" değil, aynı zamanda yığında bulunan nesnelerin yıkıcılarını da çağırır.
Adisak

@Adisak Ayrıca "yığın yürüyüş" denir bilmiyordum. Ben her zaman "yığın çözme" tüm hata ayıklayıcı makaleleri bağlamında ve hatta gdb kodu içinde gördük. Ben sadece "yığın çözme" daha uygun hissettim çünkü sadece her fonksiyon için yığın bilgisine bakmakla ilgili değil, aynı zamanda çerçeve bilgilerinin çözülmesini de içerir (cüce CFI) .Bu sırayla bir fonksiyon tek tek işlenir.
bbv

Sanırım "yığın yürüyüş" Windows tarafından daha ünlü yapılır. Ayrıca, örnek olarak buldum code.google.com/p/google-breakpad/wiki/StackWalking cüce standardının doc kendisi dışında birkaç kez gevşeme terimi kullanır. Kabul etseler de, sanal bir gevşeme. Ayrıca, soru "yığın gevşemenin" önerebileceği her olası anlam için soruluyor gibi görünüyor.
bbv

7

IMO, bu makalede verilen aşağıdaki şema , yığın çözülmesinin bir sonraki talimatın rotası üzerindeki etkisini güzelce açıklar (yakalanmamış bir istisna atıldıktan sonra yürütülecek):

resim açıklamasını buraya girin

Resimde:

  • Birincisi, normal bir çağrı yürütme işlemidir (istisna olmadan).
  • Bir istisna atıldığında birincisi.

İkinci durumda, bir istisna meydana geldiğinde, fonksiyon çağrısı yığını istisna işleyici için doğrusal olarak aranır. Arama işlevi istisna işleyici yanimain() ile try-catch, kapalı blokla , ancak işlev çağrısı yığınından önceki tüm girdileri kaldırmadan önce .


Diyagramlar iyidir ancak açıklama biraz kafa karıştırıcıdır. ... try-catch bloğu ekleyerek, ancak fonksiyon çağrısı yığınından önceki tüm girişleri kaldırmadan önce ...
Atul

3

C ++ çalışma zamanı, fırlatma ve yakalama arasında oluşturulan tüm otomatik değişkenleri yok eder. Aşağıdaki basit örnekte f1 () atar ve main () yakalamaları, B ve A tipi nesneler arasında bu sırada yığın üzerinde oluşturulur. F1 () atıldığında, B ve A yıkıcıları çağrılır.

#include <iostream>
using namespace std;

class A
{
    public:
       ~A() { cout << "A's dtor" << endl; }
};

class B
{
    public:
       ~B() { cout << "B's dtor" << endl; }
};

void f1()
{
    B b;
    throw (100);
}

void f()
{
    A a;
    f1();
}

int main()
{
    try
    {
        f();
    }
    catch (int num)
    {
        cout << "Caught exception: " << num << endl;
    }

    return 0;
}

Bu programın çıktısı

B's dtor
A's dtor

Bunun nedeni, f1 () attığında programın çağrı kaydının şöyle görünmesidir:

f1()
f()
main()

Böylece, f1 () patlatıldığında, otomatik değişken b yok edilir ve sonra f () patlatıldığında otomatik değişken a yok olur.

Umarım bu yardımcı olur, mutlu kodlama!


2

Bir istisna atıldığında ve denetim bir try bloğundan bir işleyiciye geçtiğinde, C ++ çalışma süresi try bloğunun başından bu yana oluşturulan tüm otomatik nesneler için yıkıcıları çağırır. Bu işleme yığın çözme denir. Otomatik nesneler, yapılarının tersi sırada yok edilir. (Otomatik nesneler, otomatik veya kayıt olarak bildirilen veya statik veya extern olarak bildirilmeyen yerel nesnelerdir. Otomatik x nesnesi, program x bildirildiği bloktan çıktığında silinir.)

Alt nesnelerden veya dizi öğelerinden oluşan bir nesnenin inşaatı sırasında bir istisna atılırsa, yıkıcılar yalnızca istisna oluşturulmadan önce başarıyla oluşturulan bu alt nesneler veya dizi elemanları için çağrılır. Yerel bir statik nesne için bir yıkıcı, yalnızca nesne başarılı bir şekilde oluşturulmuşsa çağrılır.


Bu yanıtı kopyaladığınız orijinal makaleye bir bağlantı sağlamalısınız: IBM Bilgi Bankası - Yığın
Çözme

0

Java yığınında unwiding veya unwounding çok önemli değil (çöp toplayıcı ile). Birçok istisna işleme kağıdında bu kavramı gördüm (yığın çözme), özellikle bu yazarlar C veya C ++ 'da istisna işleme ile ilgilenir. ile try catchblokların biz unutmak shouln't: Yerel bloklar sonra tüm nesneler arınmış yığını .

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.