Soru Ne ve yığın ve yığın nedir?


Programlama dil kitaplarında değer türlerinin oluşturulduğunu açıklar. yığınve referans türleri oluşturulur yığınBu iki şeyin ne olduğunu açıklamaksızın. Bunun net bir açıklamasını okumadım. Ne olduğunu anlıyorum bir yığın olduğunu. Fakat,

  • Nerede ve neredeler (fiziksel olarak gerçek bir bilgisayarın belleğinde)?
  • İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde denetleniyorlar?
  • Onların kapsamı nedir?
  • Her birinin boyutunu ne belirler?
  • Birini daha hızlı yapan nedir?

7126
2017-09-17 04:18


Menşei


gerçekten iyi bir açıklama burada bulunabilir Yığın ve yığın arasındaki fark nedir? - Songo
Ayrıca (gerçekten) iyi: codeproject.com/Articles/76153/... (yığın / yığın parçası) - Ben
İyi bir açıklama bulunabilir İşte - Bharat
youtube.com/watch?v=clOUdVDDzIM&spfreload=5 - Selvamani
İlgili, bkz. Yığın Clash. Yığın Çatışması düzeltmeleri, sistem değişkenlerinin ve davranışların bazı yönlerini etkiledi. rlimit_stack. Ayrıca bkz. Red Hat Sorun 1463241 - jww


Cevaplar:


Yığın, bir yürütme iş parçacığı için boş alan olarak ayrılmış bellek. Bir işlev çağrıldığında, yerel değişkenler ve bazı defter tutma verileri için yığının üstüne bir blok ayrılır. Bu işlev döndüğünde, blok kullanılmaz hale gelir ve bir sonraki işlev çağrıldığında kullanılabilir. Yığın daima bir LIFO (ilk giren sonuncu) düzeninde ayrılır; En son ayrılan blok, her zaman serbest bırakılacak bir sonraki bloktur. Bu, yığını takip etmeyi gerçekten kolaylaştırır; yığından bir blok serbest bırakmak, bir işaretçiyi ayarlamaktan başka bir şey değildir.

Yığın dinamik ayırma için ayrılmış bellek. Yığından farklı olarak, yığından blokların tahsisi ve ayrılmasında zorlanan bir model yoktur; İstediğiniz zaman bir blok ayırabilir ve istediğiniz zaman serbest bırakabilirsiniz. Bu, yığının hangi bölümlerinin tahsis edildiğini veya herhangi bir zamanda serbest kalmasını takip etmeyi çok daha karmaşık hale getirir; Farklı kullanım kalıpları için yığın performansını ayarlamak için birçok özel yığın ayırıcısı vardır.

Her bir iş parçacığı bir yığın alırken, uygulama için genellikle yalnızca bir yığın vardır (farklı türlerde ayırmalar için birden çok yığın bulunmasına karşın).

Sorularınızı doğrudan yanıtlamak için:

İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde denetleniyorlar?

İş parçacığı oluşturulduğunda işletim sistemindeki her iş parçacığı için yığın ayırır. Genel olarak işletim sistemi, uygulama için yığın ayırmak için dil çalışma zamanı tarafından çağrılır.

Onların kapsamı nedir?

Yığın bir iş parçacığına eklenir, böylece iş parçacığı çıktığı zaman yığın yeniden kazanılır. Yığın, genellikle, çalışma zamanı tarafından çalışma başlangıcında tahsis edilir ve uygulama (teknik olarak işlemden) çıktığında yeniden kullanılır.

Her birinin boyutunu ne belirler? 

Bir iş parçacığı oluşturulduğunda yığının boyutu ayarlanır. Yığının boyutu, uygulama başlangıcında ayarlanır, ancak alan gerektiğinde büyüyebilir (ayırıcı işletim sisteminden daha fazla bellek ister).

Birini daha hızlı yapan nedir?

Yığın daha hızlıdır çünkü erişim paterni bellek ayırmayı ve ondan bellek ayırmayı (bir işaretçi / tamsayı basitçe artırılır veya azalır) azaltırken, yığının bir ayırma veya ayrılma işlemiyle ilgili çok daha karmaşık bir defter tutma işlemi vardır. Ayrıca, yığındaki her bayt çok sık yeniden kullanılma eğilimindedir, bu da işlemcinin önbelleğine eşleme eğiliminde olduğu anlamına gelir ve bu da çok hızlı hale gelir. Yığın için başka bir performans isabeti, çoğunlukla bir küresel kaynak olan yığının tipik olarak çok iş parçacığı güvenli olması gerektiğidir, yani, her bir tahsisat ve ayrılmanın, genellikle - programdaki diğer tüm yığın erişimleriyle - senkronize edilmesi gerekir.

Açık bir gösteri:
Resim kaynağı: vikashazrati.wordpress.com


5239
2017-09-17 04:52



İyi cevap - ama sanırım, işlem başladığında yığın OS tarafından tahsis edilirken (bir işletim sisteminin varlığını varsayarak), program tarafından satır içi tutulur. Bu, yığının daha hızlı olmasının başka bir nedenidir - itme ve pop işlemleri genellikle bir makine talimatıdır ve modern makineler en az 3'ünü bir döngüde yapabilir, öte yandan ayırma ayırma veya boşaltma OS kodunu çağırmayı içerir. - sqykly
Sonunda şema ile gerçekten kafam karıştı. Bu resmi görene kadar anladım. - Sina Madani
@Anarelle işlemci, bir os ile veya o olmadan talimatları çalıştırır. Kalbime yakın bir örnek, hiçbir API çağrısı olmayan, bugün bildiğimiz bir işletim sistemi olmayan SNES'tir, ancak bir yığını vardı. Bir yığın üzerinde tahsis etmek, bu sistemlerde toplama ve çıkarma işlemidir ve bu, onları oluşturan işleve geri döndüklerinde yok olduklarında yok edilen değişkenler için iyidir, ancak, sonuç olarak, yalnızca bir kurucunun söz konusu olmadığı sonucuna varılabilir. atılmış Bunun için aramak ve geri dönmek için bağlı olmayan yığına ihtiyacımız var. Çoğu işletim sisteminde API'ler bir yığın, kendi başınıza yapmanıza gerek yok - sqykly
"yığın, boş alan olarak ayrılmış bellektir". Güzel. Ama aslında nerede Java bellek yapısı açısından "kenara" dir? Yığın bellek / Yığın bellek / Diğer (Java bellek yapısı başı olarak mı betsol.com/2017/06/... ) - Jatin Shashoo


Stack:

  • Bilgisayar RAM'inde yığın gibi saklandı.
  • Yığın üzerinde oluşturulan değişkenler kapsam dışı bırakılır ve otomatik olarak ayrılır.
  • Yığındaki değişkenlerle karşılaştırmak için çok daha hızlı.
  • Gerçek bir yığın veri yapısı ile uygulandı.
  • Parametre geçişi için kullanılan yerel verileri, geri dönüş adreslerini kaydeder.
  • Yığın çok fazla kullanıldığında bir yığın taşması olabilir (çoğunlukla sonsuz veya çok derin özyineleme, çok büyük tahsisler).
  • Yığın üzerinde oluşturulan veriler işaretçiler olmadan kullanılabilir.
  • Derleme zamanından önce ne kadar veri ayırmanız gerektiğini biliyorsanız ve çok büyük değilse yığını kullanabilirsiniz.
  • Genellikle programınız başladığında belirlenen maksimum bir boyuta sahiptir.

Yığın:

  • Yığın gibi bilgisayar RAMinde depolandı.
  • C ++ 'da, yığıntaki değişkenler manuel olarak yok edilmeli ve asla kapsam dışında kalmamalıdır. Veri ile serbest delete, delete[]veya free.
  • Yığındaki değişkenlerle karşılaştırmak için daha yavaş.
  • Program tarafından kullanılmak üzere bir veri bloğu tahsis etmek için talep üzerine kullanılır.
  • Çok fazla tahsis ve tahsisat olduğunda parçalanma olabilir.
  • C ++ veya C'de, yığın üzerinde oluşturulan veriler işaretçiler tarafından gösterilecek ve new veya malloc sırasıyla.
  • Bir arabelleğin çok büyük olması istenirse tahsisat hataları olabilir.
  • Çalışma zamanında tam olarak ne kadar veriye ihtiyacınız olduğunu bilmiyorsanız veya çok fazla veri ayırmanız gerekiyorsa öbek kullanabilirsiniz.
  • Bellek sızıntılarından sorumludur.

Örnek:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

2095
2017-09-17 04:20



İşaretçi pBuffer ve b değeri yığında bulunur ve büyük olasılıkla işlevin girişinde tahsis edilir. Derleyiciye bağlı olarak, işlev girişinde de arabellek tahsis edilebilir. - Andy
Bu yaygın bir yanılgıdır C tarafından tanımlanan dil C99 dil standardı open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), bir "yığın" gerektirir. Aslında, 'yığın' kelimesi standartta bile görünmez. Bu cevaplar wrt / to cevaplar Cyığın kullanımı genel olarak doğrudur, ancak dil tarafından hiçbir şekilde istenmez. Görmek knosof.co.uk/cbook/cbook.html daha fazla bilgi için ve özellikle nasıl C gibi tekdüze mimarilerde uygulanır en.wikipedia.org/wiki/Burroughs_large_systems - johne
@Brian açıklamalısın niye ya buffer [] ve pBuffer işaretçisi yığın üzerinde oluşturulur ve pBuffer'ın verileri neden yığın üzerinde oluşturulur. Programın özellikle yığının yığına yığının üzerine tahsis edilmesini, ancak durumun böyle olmadığını söyleyebildiğini düşündüğünden, bazı ppl'lerin cevabınız tarafından karıştırılabileceğini düşünüyorum. Çünkü Tampon bir değer tipidir, pBuffer ise referans tipidir? - Howiecamp
@Remover: Hiçbir işaretçi bir adresi tutar ve yığın veya yığında eşit bir şekilde bir şeye işaret edebilir. Yeni, malloc ve malloc benzeri diğer bazı işlevler yığında ayırır ve ayrılan belleğin adresini döndürür. Neden öbek üzerinde tahsis etmek istersiniz? Böylece hafızanız kapsama girmeyecek ve istediğiniz kadar serbest bırakılmayacak. - Brian R. Bondy
"Bellek sızıntılarından sorumlu" - Yığınlar bellek sızıntılarından sorumlu değildir! Tembel / Unutulmaz / ex-java kodlayıcıları / kodlayıcılar bir bok vermeyin! - Laz


En önemli nokta, yığının ve yığının, belleğin tahsis edilebileceği yollar için genel terimler olmasıdır. Birçok farklı şekilde uygulanabilirler ve şartlar temel kavramlar için geçerlidir.

  • Öğelerin bir yığınında, öğeler bir diğerinin üzerine yerleştirildikleri sıraya göre oturmaktadır ve yalnızca üstteki öğeyi kaldırabilirsiniz (tüm şeyi devirmeden).

    Stack like a stack of papers

    Bir yığının sadeliği, ayrılmış belleğin her bölümünün bir kaydını içeren bir tablo tutmanıza gerek olmamasıdır; İhtiyacınız olan tek durum bilgisi, yığının sonuna tek bir gösterici. Tahsis ve tahsis etmek için, sadece tek işaretçiyi artırıp eksiltirsiniz. Not: Bir yığın bazen bir bellek bölümünün en üstünde başlamak ve yukarı doğru büyümek yerine aşağı doğru uzanmak için uygulanabilir.

  • Bir yığında, öğelerin yerleştirilme şekli için belirli bir sıra yoktur. Herhangi bir sıraya girebilir ve öğeleri kaldırabilirsiniz, çünkü 'üst' açık bir öğe yoktur.

    Heap like a heap of licorice allsorts

    Yığın tahsisi, belleğin ayrıldığı ve neyin olmadığıyla ilgili tam bir kaydı tutmayı ve parçalanmayı azaltmak için bazı genel bakım gereksinimlerini, istenen boyuta uyacak kadar büyük olan bitişik bellek bölümlerini bulmayı gerektirir. Hafıza, boş alan bırakarak herhangi bir zamanda tahsis edilebilir. Bazen bir bellek ayırıcı, ayrılmış bellekte dolaşarak veya çöp toplama işlemiyle bellek birleştirmek gibi bakım görevlerini gerçekleştirir - bellek artık kapsamda olmadığında ve yeniden ayırma sırasında çalışma zamanında tanımlanır.

Bu görüntüler, bir yığın ve yığın içinde bellek ayırma ve boşaltma iki yolunu açıklayan oldukça iyi bir iş yapmalıdır. Yum!

  • İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde denetleniyorlar?

    Belirtildiği gibi, yığın ve yığın genel terimlerdir ve birçok şekilde uygulanabilir. Bilgisayar programları genellikle bir ad verilir çağrı yığını Hangi işlevden çağrıldığına ve herhangi bir yerel değişkene işaretçi gibi geçerli işlevle ilgili bilgileri saklar. İşlevler diğer işlevleri çağırıp geri döndüğünden, yığın, bilgi yığınından aşağı doğru işlevlerden bilgi tutacak şekilde büyür ve küçülür. Bir program üzerinde çalışma zamanı kontrolü bulunmuyor; Programlama dili, işletim sistemi ve hatta sistem mimarisi tarafından belirlenir.

    Bir yığın, dinamik ve rastgele ayrılmış herhangi bir bellek için kullanılan genel bir terimdir; yani sıra dışı. Bellek genellikle işletim sistemi tarafından tahsis edilir, uygulama çağırma API'si bu ayırmayı yapmak için işlev görür. Genellikle işletim sistemi tarafından işlenen dinamik olarak ayrılmış belleğin yönetilmesi için gerekli olan biraz yük vardır.

  • Onların kapsamı nedir?

    Çağrı yığını, programlama anlamında 'kapsam' ile ilgili olmayan düşük seviyede bir kavramdır. Bazı kodları söndürürseniz, yığının bölümleri için göreli işaretçi stili referansları görürsünüz, ancak daha yüksek düzeyli bir dil söz konusu olduğunda, dil kendi kapsam kurallarını dayatır. Bununla birlikte, bir yığının önemli bir yönü, bir işlev döndüğünde, bu işleve yerel olan herhangi bir şeyin derhal yığından serbest bırakılmasıdır. Programlama dillerinin nasıl çalıştığı göz önüne alındığında çalışma şeklini çalışır. Bir yığın halinde tanımlamak da zordur. İşletim sistemi, işletim sistemi tarafından açık olan her şeydir, ancak programlama diliniz muhtemelen uygulamanızda "kapsam" ın ne olduğuyla ilgili kurallarını ekler. İşlemci mimarisi ve işletim sistemi, işlemcinin fiziksel adreslere çevrildiği ve sayfa hataları vb. Olduğu sanal adreslemeyi kullanır. Hangi sayfaların hangi uygulamalara ait olduğunu takip ederler. Bununla birlikte, bunun için endişelenmenize gerek yok, çünkü, programlama dilinizin, bellek ayırmak ve boşaltmak için kullandığı herhangi bir yöntemi kullanıyorsunuz ve hataları denetlemeniz (herhangi bir nedenle ayırma / boşaltma başarısız olursa).

  • Her birinin boyutunu ne belirler?

    Yine, dil, derleyici, işletim sistemi ve mimariye bağlıdır. Bir yığın genellikle önceden tahsis edilir, çünkü tanım gereği bitişik bellek olmalıdır (son paragrafta daha fazladır). Dil derleyici veya işletim sistemi boyutunu belirler. Yığın üzerine çok büyük miktarda veri depolamıyorsunuz, bu nedenle istenmeyen sonsuz yineleme (dolayısıyla "yığın taşması") veya diğer sıradışı programlama kararları durumları dışında asla tam olarak kullanılmaması için yeterince büyük olacaktır.

    Bir yığın, dinamik olarak ayrılabilen her şey için genel bir terimdir. Hangi yöne baktığınıza bağlı olarak, sürekli değişen boyuttadır. Modern işlemcilerde ve işletim sistemlerinde, çalışmanın tam yolu zaten çok soyutlanmıştır, bu yüzden normalde nasıl derinden çalıştığı konusunda endişelenmenize gerek yoktur, bunun dışında (size izin verdiği dillerde) Henüz tahsis etmediniz ya da özgürlüğünüzü kaybettiniz.

  • Birini daha hızlı yapan nedir?

    Yığın daha hızlıdır, çünkü tüm boş hafıza her zaman bitişiktir. Boş hafızanın tüm bölümlerinde hiçbir listenin tutulması gerekmiyor, sadece yığının mevcut üstüne tek bir işaretçi. Derleyiciler genellikle bu göstericiyi özel, hızlı bir şekilde depolar. kayıt olmak bu amaç için. Dahası, bir yığın üzerindeki sonraki işlemler genellikle çok yakın bir seviyede bellek üzerinde yer alırlar, ki bu çok düşük bir seviyede işlemci üzerindeki önbelleklerin optimizasyonu için iyidir.


1261
2018-03-19 14:38



David Bunun iyi bir görüntü olduğuna ya da "aşağı itilmiş yığın" kavramını göstermek için iyi bir terim olduğuna katılıyorum. Yığına bir şey eklediğinizde, yığının diğer içeriği değil aşağı itti, oldukları yerde kalıyorlar. - thomasrutter
Bu cevap büyük bir hata içeriyor. Statik değişkenler yığına ayrılmaz. Cevabımı gör [link] stackoverflow.com/a/13326916/1763801 netleştirmek adına. "statik" değişkenlerle "otomatik" değişkenleri eşitliyorsunuz, ama hepsi aynı değil - davec
Özellikle, "statik olarak ayrılmış yerel değişkenler" yığında yer alırsınız. Aslında veri segmentine ayrılmıştır. Sadece otomatik olarak tahsis edilen değişkenler (çoğu yerel değişkenleri içermeyen fakat bunlara değil, aynı zamanda referansa göre değer olarak iletilen işlev parametreleri gibi) yığına dağıtılır. - davec
Sadece doğru olduğunu anladım - C’de, statik ayırma olmayan bir şeyden ziyade kendi ayrı bir şeydir dinamik. Cevabımı düzenledim, teşekkürler. - thomasrutter
Sadece C. Java değil, Pascal, Python ve diğerleri, statik veya otomatik olarak dinamik ayırma kavramlarına sahiptir. "Statik tahsis" demek hemen hemen her yerde aynı şeyi ifade eder. Hiçbir dilde statik tahsis, "dinamik değil" anlamına gelir. Tanımladığınız şey için "otomatik" ayırma deyimini (yani, yığındaki şeyleri) istiyorsunuz. - davec


(Bu cevabı, bunun bir ya da daha az bir kopyası olan başka bir sorudan aldım.)

Sorunuzun yanıtı uygulamaya özgüdür ve derleyiciler ve işlemci mimarileri arasında farklılık gösterebilir. Ancak, burada basitleştirilmiş bir açıklama.

  • Hem yığın hem de yığın, temeldeki işletim sisteminden ayrılmış bellek alanlarıdır (genellikle talep üzerine fiziksel belleğe eşlenen sanal bellek).
  • Çok iş parçacıklı bir ortamda, her iş parçacığının kendi bağımsız bağımsız yığını olur, ancak öbekleri paylaşırlar. Eşzamanlı erişim yığında kontrol edilmeli ve istifte mümkün değil.

Yığın

  • Yığın, kullanılmış ve ücretsiz blokların bağlantılı bir listesini içerir. Yığındaki yeni tahsisler new veya malloc) serbest bloklardan birinden uygun bir blok oluşturarak tatmin edilir. Bu, yığıntaki blok listelerinin güncellenmesini gerektirir. Bu meta bilgisi Yığın blokları hakkında, her blokun hemen önünde küçük bir alanda genellikle yığın üzerinde saklanır.
  • Yığın büyüdükçe yeni bloklar genellikle alt adreslerden daha yüksek adreslere doğru dağıtılır. Böylece, öbek olarak düşünebilirsiniz. yığın bellek olarak boyut olarak büyütülen bellek blokları tahsis edilir. Yığın bir tahsis için çok küçükse, boyut, temeldeki işletim sisteminden daha fazla bellek elde etmek suretiyle arttırılabilir.
  • Birçok küçük bloğun tahsis edilmesi ve ayrılması, yığının, kullanılmış bloklar arasında serpiştirilmiş çok sayıda küçük serbest blokların bulunduğu bir durumda bırakılabilir. Büyük bir bloğun tahsisi için bir talep başarısız olabilir çünkü serbest blokların hiçbiri, serbest blokların birleşik boyutu yeterince büyük olsa bile, tahsis talebini karşılayacak kadar büyük değildir. Buna denir yığın parçalanması.
  • Serbest bir bloğa bitişik olan kullanılmış bir blok serbest bırakıldığında, yeni serbest blok, yığının fragmantasyonunu etkili bir şekilde azaltarak daha büyük bir serbest blok oluşturmak için bitişik serbest blok ile birleştirilebilir.

The heap

Yığın

  • Yığın sık sık CPU adlı özel bir kayıt ile yakın tandem çalışır yığın işaretçisi. Başlangıçta yığın gösterici yığının üst kısmına işaret eder (yığındaki en yüksek adres).
  • CPU'nun özel talimatları vardır. itme yığının üstüne değerler ve haşhaş onları yığından geri. Her it değeri yığın işaretçisinin geçerli konumuna kaydeder ve yığın işaretçisini azaltır. bir pop yığın işaretçisinin işaret ettiği değeri alır ve yığın işaretçisini artırır. ekleme yığına bir değer azalır yığın işaretçisi ve çıkarmadan bir değer artışlar o. Yığının dibe doğru uzandığını unutmayın). Kaydedilen ve alınan değerler CPU kayıtlarının değerleridir.
  • Bir işlev çağrıldığında, CPU akımı iten özel yönergeler kullanır. talimat işaretçisiyani yığın üzerinde yürüten kodun adresi. CPU daha sonra, yönerge işlevi çağrılan işlevin adresine. Daha sonra, işlev döndüğünde, eski talimat işaretçisi yığından atılır ve yürütme, işleve yapılan çağrıdan hemen sonra kodda devam eder.
  • Bir işlev girildiğinde, yerel (otomatik) değişkenler için yığın üzerinde daha fazla yer ayırmak için yığın işaretçisi azaltılır. İşlevde bir yerel 32 bit değişken varsa, yığında dört bayt bir kenara ayarlanır. İşlev döndüğünde, yığın işaretçisi ayrılan alanı serbest bırakmak için geri taşınır.
  • Bir fonksiyonun parametreleri varsa, bunlar fonksiyon çağrısından önce yığının üzerine itilir. İşlevdeki kod daha sonra bu değerleri bulmak için yığının mevcut yığın işaretçisinden yukarı doğru hareket etmesini sağlar.
  • Yerleştirme işlevi çağrıları bir çekicilik gibi çalışır. Her yeni çağrı, işlev değişkenleri, geri dönüş adresi ve yerel değişkenler için alan tahsis edecektir. aktivasyon kayıtları iç içe geçmiş çağrılar için istiflenebilir ve işlevler geri döndüğünde doğru şekilde gevşer.
  • Yığın sınırlı bir bellek bloğu olduğundan, yığın taşması çok fazla iç içe geçmiş işlevi çağırmak ve / veya yerel değişkenler için çok fazla alan ayırmak. Genellikle yığın için kullanılan bellek alanı, yığının alt kısmının (en düşük adres) altına yazmanın CPU'da bir tuzak veya istisna tetikleyeceği şekilde ayarlanır. Bu istisnai durum daha sonra çalışma zamanı tarafından yakalanabilir ve bir tür yığın taşması istisnasına dönüştürülebilir.

The stack

Yığın yerine yığın yerine bir işlev atanabilir mi?

Hayır, işlevler için aktivasyon kayıtları (yani yerel veya otomatik değişkenler), yalnızca bu değişkenleri depolamak için değil, aynı zamanda iç içe geçmiş işlev çağrılarını takip etmek için kullanılan yığına yerleştirilir.

Yığın nasıl yönetilir, gerçekten çalışma zamanı ortamına bağlıdır. C kullanır malloc ve C ++ kullanır newama diğer birçok dilde çöp toplama var.

Bununla birlikte, yığın işlemci mimarisine sıkı sıkıya bağlı daha düşük seviyeli bir özelliktir. Yeterli alan olmadığı zaman yığının büyümesi, yığının işlenmesini sağlayan kitaplık çağrısında uygulanabileceğinden çok fazla zor değildir. Bununla birlikte, yığının taşması sadece çok geç olduğunda keşfedildiğinden yığının büyütülmesi genellikle imkansızdır; ve yürütme iş parçacığını kapatmak tek geçerli seçenektir.


664
2017-07-31 15:54



@Martin - Daha fazla kabul edilen cevaptan çok iyi bir cevap / açıklama. Bir vekil işlev çağrıları ile kullanılan yığın işaretleyicileri / yazmaçlarını gösteren bir örnek montaj programı daha açıklayıcı olacaktır. - Bikal Lem
Her referans tipi, değer tiplerinin (int, string vb.) Kompozisyonudur. Söylendiği gibi, bu değer türleri, referans türünün parçası olduklarında nasıl çalıştığından daha fazla yığın halinde depolanır. - Nps
Bu cevap benim görüşüme göre en iyisiydi, çünkü bir geri dönüş bildiriminin gerçekte ne anlama geldiğini ve her biriyle karşılaştığım bu "geri dönüş adresi" ile nasıl bir ilişki kurduğumu ve bir işlevi yığının üzerine itmek için ne anlama geldiğini anlamamda yardımcı oldu. ve işlevler yığınlara neden itilir? Mükemmel cevap! - Alex
Bu benim düşüncemde en iyisi, yani yığının / yığınının çok uygulamaya özgü. Diğer cevaplar bir çok dil ve çevre / OS ile ilgili şeylerin. +1 - Qix
Ne demek "Bu fonksiyondaki kod daha sonra bu değerleri bulmak için mevcut yığın göstergesinden yığına gidebilir." ? Bu konuda ayrıntılı bilgi verir misiniz lütfen? - Koray Tugay


Aşağıdaki C # kodunda

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Hafıza nasıl yönetilir

Picture of variables on the stack

Local Variables Sadece işlev çağırma yığınının içine girdiği sürece devam etmesi gerekir. Yığın, ömrünü gerçekten bilmediğimiz değişkenler için kullanıyor, ancak bir süre dayanmasını bekliyoruz. Çoğu dilde derleme zamanında bilmemiz kritiktir, eğer bir yığının yığında saklamak istiyorsak ne kadar büyük olduğunu.

Nesneler (onları güncelledikçe boyut olarak değişir) yığına girer çünkü yaratılış süresinde ne kadar süreceklerini bilmiyoruz. Birçok dilde yığın artık herhangi bir referansı olmayan nesneleri (cls1 nesnesi gibi) bulmak için toplanır.

Java'da, çoğu nesne doğrudan yığına gider. C / C ++ gibi dillerde, işaretçilerle uğraşmadığınız zaman, yapılar ve sınıflar genellikle yığında kalabilir.

Daha fazla bilgi burada bulunabilir:

Yığın ve yığın bellek ayırma arasındaki fark «timmurphy.org

ve burada:

Yığın ve Yığında Nesneler Yaratmak

Bu makale yukarıdaki resmin kaynağıdır: Altı önemli .NET kavramı: Yığın, yığın, değer türleri, referans türleri, boks ve kutuklama - CodeProject

ancak bazı yanlışlıklar içerebileceğinin farkında olun.


352
2017-11-09 12:28



Bu yanlış. Ben ve cl'ler "statik" değişkenler değildir. "yerel" veya "otomatik" değişkenler olarak adlandırılırlar. Bu çok önemli bir ayrımdır. Bakın [bağlantı] stackoverflow.com/a/13326916/1763801 netleştirmek adına - davec
Statik olduklarını söylemedim değişkenler. İnt ve cls1'in statik olduğunu söyledim ürün. Hafızası statik olarak tahsis edilir ve bu nedenle yığına giderler. Bu, öbek üzerinde yer alan dinamik bellek ayırma gerektiren bir nesnenin tersidir. - Snowcrash
"Statik öğeler ... yığına git" yazdım. Bu sadece yanlış anlaşıldı. Statik öğeler veri segmentine gider, otomatik eşyalar yığına gider. - davec
Ayrıca, bu kod makalesini yazan kişi, ne hakkında konuştuğunu bilmez. Mesela, "ilkellerin statik tip hafızasına ihtiyacı var" diyor ki bu tamamen doğru değil. Hiçbir şey yığın içinde ilkelleri dinamik olarak ayırmaktan vazgeçirir, sadece "int dizisi [] = new int [num]" gibi bir şey yazabilir ve voila, .NET'te dinamik olarak ayrılmış primitifler yazabilirsiniz. Bu sadece birkaç yanlıştan biridir. - davec
Gönderinizi düzenledim çünkü yığınta ve yığında ne olduğu konusunda ciddi teknik hatalar yaptınız. - Tom Leys


Yığın Bir işlevi çağırdığınızda, o işleve karşı argümanlar artı başka bir ek yük yığının üstüne konur. Bazı bilgiler (geri dönüşün yapıldığı yer gibi) de burada saklanır. İşlevin içinde bir değişken bildirdiğinizde, bu değişken de yığında ayrılır.

Yığının ayrılması oldukça basittir, çünkü her zaman ayırdığınız ters sırada ayrılırsınız. Yığınlar, fonksiyonlar girerken eklenir, bunlardan çıkarken ilgili veriler silinir. Bu, diğer birçok işlevi çağıran çok sayıda işlev çağırmazsanız (veya özyinelemeli bir çözüm oluşturuyorsanız) yığının küçük bir bölgesinde kalmaya eğilimlidir.

Yığın Yığın, oluşturduğunuz verileri anında yayınladığınız genel bir addır. Programınızın kaç tane uzay alanı yaratacağını bilmiyorsanız, her bir uzay gemisi oluşturmak için yeni (veya malloc veya eşdeğer) operatörü kullanmanız muhtemeldir. Bu tahsis bir süreliğine etrafta dolaşacak, bu yüzden onları yarattığımızdan farklı bir düzende özgürleştireceğiz.

Böylelikle yığın çok daha karmaşıktır, çünkü bellekler parçalanmış belleklerle parçalanmış olan bellek bölgeleridir - bellek parçalanır. İhtiyacınız olan boyutta boş hafıza bulmak zor bir sorundur. Bu yüzden yığından kaçınılmalıdır (yine de sıklıkla kullanılır).

uygulama Hem yığın hem de yığının uygulanması genellikle çalışma zamanı / işletim sistemine geçer. Çoğu zaman, performans açısından kritik olan oyunlar ve diğer uygulamalar, bellekte büyük bir yığın bellek alan kendi bellek çözümlerini oluşturur ve daha sonra bellekteki işletim sistemine güvenmekten kaçınmak için dahili olarak dışarıda yerleştirir.

Bu sadece bellek kullanımınız normdan oldukça farklıysa, yani büyük bir operasyonda bir seviye yüklediğiniz ve diğer büyük bir operasyonda bir sürü yükü kaldırabileceğiniz oyunlar için pratiktir.

Bellekte fiziksel konum Bu, teknolojiden dolayı düşündüğünüzden daha az alakalı Sanal bellek Programınız, fiziksel verilerin başka bir yerde (sabit diskte bile!) bulunduğu belirli bir adrese erişebildiğinizi düşünür. Yığın için aldığınız adresler, arama ağacınız daha da derinleştikçe artar. Yığın adresleri önceden tahmin edilemez (yani spesifikasyona özgüdür) ve açıkçası önemli değildir.


191
2017-09-17 04:27



Yığını kullanmamak için bir öneri oldukça güçlüdür. Modern sistemler iyi yığın yöneticilerine sahiptir ve modern dinamik diller yığınları yaygın olarak kullanırlar (programcı gerçekten endişe duymadan). Yığını kullanayım, ama manuel ayırıcı ile özgür olmayı unutma! - Greg Hewgill
Yığını veya yığını kullanabilirsiniz, yığını kullanın. Yığını kullanamazsan, gerçekten başka seçenek yok. Ben hem çok kullanırım, hem de tabiki std :: vector veya benzerlerini kullanırım. Bir acemi için, yığın yığını çünkü yığın sadece çok kolay! - Tom Leys
Diliniz çöp toplama işlemini gerçekleştirmiyorsa, Akıllı işaretçiler (Dinamik olarak ayrılmış bellek parçaları için referans sayımı yapan bir işaretçiyi çevreleyen ayrı ayrı ayrılmış nesneler) çöp toplama ile yakından ilişkilidir ve öbeği güvenli bir şekilde yönetmenin iyi bir yoludur ve serbest bir şekilde. Çeşitli çerçevelerde uygulanmaktadır, ancak kendi programlarınız için de uygulamak zor değildir. - BenPen
"Bu yüzden yığından kaçınılmalıdır (yine de sıklıkla kullanılır)." Bunun pratikte ne anlama geldiğinden emin değilim, özellikle de bellek birçok üst düzey dilde farklı şekilde yönetiliyor. Bu soru dil-agnostik olarak etiketlendiğinden, bu özel yorumun / satırın yanlış yerleştirildiğini ve uygulanamaz olduğunu söyleyebilirim. - JonnoHampson
İyi bir nokta @JonnoHampson - Geçerli bir noktayı oluştururken, bir GC ile "yüksek düzeyli bir dilde" çalışıyorsanız muhtemelen bellek ayırma mekanizmalarını hiç umursamıyorsunuzdur - hatta yığının ve yığının ne olduğuna dikkat et. - Tom Leys


Netleşmeştirmek, bu cevap yanlış bilgi varthomas Cevabını yorumladıktan sonra düzeltti, :)). Diğer cevaplar sadece statik tahsisin ne anlama geldiğini açıklamaktan kaçınıyor. Bu yüzden, üç ana dağıtım biçimini ve genellikle aşağıdaki yığın, yığın ve veri segmentiyle nasıl ilişkilendiklerini açıklayacağım. Ayrıca C / C ++ ve Python'da insanların anlamasına yardımcı olmak için bazı örnekler göstereceğim.

"Statik" (AKA statik olarak tahsis edilmiş) değişkenleri yığına ayrılmaz. Öyle düşünmeyin - pek çok insan sadece "statik" "yığın" gibi çok fazla olduğu için yapar. Aslında ne yığında ne de yığında var. Bunlar ne denir veri segmenti.

Ancak, genellikle dikkate almak daha iyidir "kapsam" ve "ömür"yığın" ve "yığın" yerine.

Kapsam, kodun hangi bölümlerinin bir değişkene erişebileceğini belirtir. Genel olarak düşünürüz yerel kapsam (sadece geçerli işlev tarafından erişilebilir) küresel kapsam kapsamı çok daha karmaşık olsa da (her yerden erişilebilir).

Ömür boyu, program yürütme sırasında bir değişkenin ne zaman tahsis edildiğini ve ayrıldığını belirtir. Genellikle düşünürüz statik ayırma (değişken, programın tüm süresi boyunca devam eder, bu da aynı bilgiyi çeşitli işlev çağrıları boyunca saklamak için yararlı hale getirir) otomatik ayırma (değişken sadece bir fonksiyona tek bir çağrı sırasında devam eder, sadece sizin fonksiyonunuz sırasında kullanılan bilgileri saklamanızı ve işiniz bittikten sonra atılabilir) dinamik ayırma (süresi çalışma zamanında tanımlanmış, statik veya otomatik gibi derleme zamanı yerine).

Çoğu derleyici ve yorumlayıcı, bu davranışı yığın, yığın, vb. Kullanarak benzer şekilde uygulasa da, derleyici, davranış doğru olduğu sürece istediği zaman bu kuralları ihlal edebilir. Örneğin, optimizasyon nedeniyle yerel bir değişken sadece bir kayıtta mevcut olabilir ya da çoğu yerel değişken yığında var olsa bile tamamen kaldırılabilir. Birkaç yorumda belirtildiği gibi, bir yığın veya yığın bile kullanmayan bir derleyiciyi uygulamakta özgürsünüz, bunun yerine bazı depolama mekanizmaları (yığınlar ve yığınlar bunun için çok büyük olduğundan nadiren yapılır).

Tüm bunları göstermek için bazı basit açıklamalı C kodları vereceğim. Öğrenmenin en iyi yolu bir programı bir hata ayıklayıcısında çalıştırmak ve davranışı izlemek. Eğer python okumayı tercih ederseniz, cevabın sonuna gelin :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Yaşam süresi ile kapsamı ayırt etmenin neden önemli olduğunun özellikle dokunaklı bir örneği, bir değişkenin yukarıdaki kod örneğinde yerel kapsamı ancak statik ömrüne sahip olabilmesidir - örneğin, "someLocalStaticVariable". Bu değişkenler bizim ortak ama gayri resmi isimlendirme alışkanlıklarımızı çok kafa karıştırıcı yapabilirler. Mesela dediğimiz zaman "yerel"biz genellikle"yerel kapsamlı otomatik ayrılmış değişken"ve küresel dediğimiz zaman genellikle"küresel kapsamlı istatistiksel olarak ayrılmış değişken". Ne yazık ki böyle şeyler söz konusu olduğunda"dosya, statik olarak ayrılmış değişkenler"Birçok insan sadece ..."öyle ???".

C / C ++ 'daki bazı sözdizimi seçenekleri bu sorunu şiddetlendirir - örneğin, birçok kişi aşağıda gösterilen sözdizimi nedeniyle global değişkenlerin "statik" olmadığını düşünmektedir.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Yukarıdaki beyannamede "statik" anahtar kelimesini koymak, var2'nin genel kapsamı olmasını önler. Yine de, global var1 statik tahsisat var. Bu sezgisel değil! Bu nedenle, kapsamı açıklarken "statik" kelimesini asla kullanmaya çalışmam ve bunun yerine "dosya" veya "dosya sınırlı" alanı gibi bir şey söyleyeceğim. Ancak, birçok kişi yalnızca bir kod dosyasından erişilebilen bir değişkeni tanımlamak için "statik" veya "statik kapsam" deyimini kullanır. Ömür boyu, "statik" bağlamında her zaman değişken program başlangıcında tahsis edilir ve program çıktığında ayrılır.

Bazı insanlar bu kavramları C / C ++ spesifik olarak düşünür. Onlar değil. Örneğin, aşağıdaki Python örneği, her üç tahsis türünü göstermektedir (buraya girmeyeceğim yorumlanmış dillerden bazı ince farklılıklar olabilir).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

169
2017-09-17 04:48



Bir işlev içinde bildirilen statik bir değişkeni yalnızca yerel olarak belirtmek isterim ulaşılabilirlikama genellikle "kapsam" terimini onunla kullanmaz. Ayrıca, dillerin temelde sıfır esnekliğe sahip olduğu yığın / yığın yönünün de dikkate alınması gerekir: bir yığın üzerinde yürütme bağlamını kaydeden bir dil, oluşturuldukları bağlamları aşmak zorunda kalacak şeyleri tutmak için aynı yığını kullanamaz. . Bazı diller gibi PostScriptBirden fazla yığın var, ama bir yığın gibi davranan bir "yığın" var. - supercat
@supercat Hepsi mantıklı. Kapsamı "kodun hangi kısımları" olarak tanımladım erişim bir değişken "(ve bunun en standart tanım olduğunu hissediyorum) bu yüzden katılıyorum diye düşünüyorum :) - davec
Bir "kapsamını" dikkate alacağım değişken zamanın yanı sıra mekân tarafından sınırlanmış olarak. Nesne var olduğu sürece değerini tutmak için sınıf nesnesi kapsamındaki bir değişken gereklidir. Yürütme bağlamı kapsamındaki bir değişken, yürütme bu bağlamda kaldığı sürece değerini tutmak için gereklidir. Statik değişken beyanı bir tanımlayıcı kapsamı mevcut bloğa sınırlanır; değişken kapsamı sınırsız. - supercat
@supercat Bu yüzden kelime kullanım ömrünü kullanıyorum, bu yüzden zaman kapsamı dediğiniz terimdir. Çok fazla anlamla "kapsam" kelimesini aşırı yükleme ihtiyacını azaltır. Anlayabildiğim kadarıyla, kanonik kaynaklar arasında bile kesin tanımlamalar konusunda tam bir fikir birliği görülmemektedir. Benim terminolojim kısmen K & R'dan ve kısmen de çalıştım / öğretilen ilk CS bölümündeki yaygın kullanımdan alınmıştır. Başka bir bilgilendirilmiş görüş duymak her zaman iyi. - davec
Dalga geçiyor olmalısın. Bir fonksiyon içindeki statik değişkeni gerçekten tanımlayabilir misiniz? - Zaeem Sattar


Diğerleri geniş vuruşlara oldukça iyi cevap verdiler, bu yüzden birkaç ayrıntıya geçeceğim.

  1. Yığın ve yığının tekil olması gerekmez. Birden fazla yığına sahip olduğunuz yaygın bir durum, bir işlemde birden fazla iş parçacığınız varsa. Bu durumda, her bir iş parçacığının kendi yığını vardır. Aynı zamanda birden fazla yığınınız olabilir, örneğin bazı DLL yapılandırmaları farklı yığınlardan farklı DLL'lere yol açabilir, bu yüzden genellikle farklı bir kütüphane tarafından tahsis edilen belleği serbest bırakmak kötü bir fikirdir.

  2. C ile değişken uzunluk tahsisi yararını kullanarak allocaYığın üzerine tahsis edilen tahsisatın aksine yığına tahsis edilir. Bu bellek geri dönüş ifadenizde geçerli olmayacaktır, ancak bir çizik tamponu için kullanışlıdır.

  3. Windows üzerinde çok fazla kullanmamanız gereken büyük geçici bir tampon yapmak ücretsiz değildir. Bunun nedeni, derleyicinin yığının var olduğundan emin olmak için işlevinizin her girilmesi sırasında çağrılan bir yığın probu döngüsü oluşturmasıdır (çünkü yığınının büyümesi gerektiğinde algılamak için Windows yığınınızın sonunda tek bir koruma sayfası kullanır. Hafızaya erişirseniz, yığının sonundaki bir sayfadan daha fazlası çökecektir). Örnek:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

156
2017-09-17 07:16



“Tahliyenin aksine”: “Malloğun aksine” demek istiyor musunuz? - Peter Mortensen
Ne kadar taşınabilir alloca? - Peter Mortensen
@PeterMortensen POSIX değil, taşınabilirlik garanti edilmez. - Don Neufeld