Yığın yukarı veya aşağı doğru büyüyor mu?


90

Bu kod parçasını c:

Çıktı:

Yani, o bkz aüzere a[2], 4 ile bellek adresleri artar her bayt. Ancak qila arasında s, bellek adresleri 4 bayt azalır.

2 şeyi merak ediyorum:

  1. Yığın büyüyor mu azalıyor mu? (Bu durumda ikisine de benziyor)
  2. a[2]Ve qhafıza adresleri arasında ne olur ? Neden orada büyük bir hafıza farkı var? (20 bayt).

Not: Bu ev ödevi sorusu değildir. Yığının nasıl çalıştığını merak ediyorum. Herhangi bir yardım için teşekkürler.


Sıralama keyfi. Boşluk muhtemelen & q veya & s gibi bir ara sonucu depolamaktır - sökme işlemine bakın ve kendiniz görün.
Tom Leys

Kabul ediyorum, montaj kodunu okuyun. Bu tür sorular soruyorsanız, okumayı öğrenme zamanı.
Per Johansson

Yanıtlar:


74

Yığının davranışı (büyüme veya küçülme) uygulama ikili arabirimine (ABI) ve çağrı yığınının (aka aktivasyon kaydı) nasıl organize edildiğine bağlıdır.

Kullanım ömrü boyunca bir program, işletim sistemi gibi diğer programlarla iletişim kurmaya mecburdur. ABI, bir programın başka bir programla nasıl iletişim kurabileceğini belirler.

Farklı mimariler için yığın her iki şekilde de büyüyebilir, ancak bir mimari için tutarlı olacaktır. Lütfen bu wiki bağlantısını kontrol edin . Ancak yığının büyümesine o mimarinin ABI'si tarafından karar verilir.

Örneğin, MIPS ABI alırsanız, çağrı yığını aşağıdaki gibi tanımlanır.

"Fn1" işlevinin "fn2" yi çağırdığını düşünelim. Şimdi, 'fn2'de görüldüğü gibi yığın çerçevesi aşağıdaki gibidir:

Şimdi yığının aşağı doğru büyüdüğünü görebilirsiniz. Dolayısıyla, değişkenler fonksiyonun yerel çerçevesine tahsis edilirse, değişkenin adresleri aslında aşağı doğru büyür. Derleyici, bellek tahsisi için değişkenlerin sırasına karar verebilir. (Sizin durumunuzda, ilk ayrılmış yığın belleği olan 'q' veya 's' olabilir. Ancak, genellikle derleyici, değişkenlerin bildirim sırasına göre yığın bellek ayırma işlemi yapar).

Ancak diziler söz konusu olduğunda, tahsisin yalnızca tek bir göstericisi vardır ve ayrılması gereken bellek aslında tek bir gösterici tarafından gösterilecektir. Bir dizi için belleğin bitişik olması gerekir. Yani, yığın aşağı doğru büyümesine rağmen, diziler için yığın büyür.


5
Ek olarak, yığının yukarı veya aşağı doğru büyüyüp büyümediğini kontrol etmek istiyorsanız. Ana işlevde yerel bir değişken bildirin. Değişkenin adresini yazdırın. Main'den başka bir işlevi çağırın. İşlevde yerel bir değişken bildirin. Adresini yazdırın. Yazdırılan adreslere göre yığının yukarı veya aşağı büyüdüğünü söyleyebiliriz.
Ganesh Gopalasubramanian

teşekkürler Ganesh, küçük bir sorum var: u çizdiğim şekilde, üçüncü blokta, "calleR kaydedilmiş yazmacı CALLER'da kullanılıyor" mu demek istedin, çünkü f1 f2'yi çağırdığında, f1 adresini saklamak zorundayız (dönüş adresi f2) ve f1 (calleR) kayıtları için f2 (aranan) kayıtları değil. Sağ?
CSawy

43

Bu aslında iki sorudur. Biri, bir fonksiyon diğerini çağırdığında (yeni bir çerçeve tahsis edildiğinde) yığının hangi yolla büyüdüğüyle , diğeri ise değişkenlerin belirli bir fonksiyon çerçevesinde nasıl yerleştirildiğiyle ilgilidir.

C standardı tarafından hiçbiri belirtilmemiştir, ancak cevaplar biraz farklıdır:

  • Yeni bir çerçeve tahsis edildiğinde yığın hangi şekilde büyür - f () fonksiyonu g () fonksiyonunu çağırırsa, fçerçeve işaretçisi çerçeve işaretçisinden büyük veya küçük olur gmu? Bu her iki şekilde de olabilir - belirli derleyiciye ve mimariye bağlıdır ("arama kuralına" bakın), ancak belirli bir platformda her zaman tutarlıdır (birkaç tuhaf istisna dışında yorumlara bakın). Aşağı doğru daha yaygındır; x86, PowerPC, MIPS, SPARC, EE ve Hücre SPU'larında durum böyledir.
  • Bir işlevin yerel değişkenleri yığın çerçevesi içinde nasıl düzenlenir? Bu belirtilmemiş ve tamamen tahmin edilemez; derleyici yerel değişkenlerini düzenlemekte özgürdür, ancak en verimli sonucu almayı sever.

7
"belirli bir platform içinde her zaman tutarlıdır" - garanti edilmez. Yığının dinamik olarak genişletildiği, sanal belleği olmayan bir platform gördüm. Yeni yığın blokları gerçekte hafifletildi, yani bir süre bir yığın bloğu "aşağı", sonra aniden farklı bir bloğa "yanlamasına" gideceksin. "Yanlara doğru", tamamen çekiliş şansına bağlı olarak daha büyük veya daha küçük bir adres anlamına gelebilir.
Steve Jessop

2
Madde 2'ye ek ayrıntı için - bir derleyici, bir değişkenin asla bellekte olması gerekmediğine (değişkenin ömrü boyunca bir kayıt defterinde tutulması) ve / veya iki veya daha fazla değişkenin yaşam süresinin uygun olmadığına karar verebilir. t örtüştüğünde, derleyici aynı belleği birden fazla değişken için kullanmaya karar verebilir.
Michael Burr

2
Bence S / 390 (IBM zSeries), bir yığın üzerinde büyümek yerine çağrı çerçevelerinin bağlandığı bir ABI'ye sahip.
ephemient

2
S / 390'da doğru. Çağrı "BALR", şube ve bağlantı kaydıdır. Dönüş değeri, bir yığına itilmek yerine bir kayda yerleştirilir. Dönüş işlevi, bu kaydın içeriğine bir daldır. Yığın derinleştikçe, yığın içinde alan ayrılır ve bunlar birbirine zincirlenir. "/ Bin / true" 'nun MVS eşdeğeri, adını aldığı yerdir: "IEFBR14". İlk versiyonun tek bir talimatı vardı: "BR 14", geri dönüş adresini içeren kayıt 14'ün içeriğine daldı.
janm

1
Ve PIC işlemcilerindeki bazı derleyiciler tüm program analizi yapar ve her işlevin otomatik değişkenleri için sabit konumlar tahsis eder; gerçek yığın küçüktür ve yazılımdan erişilemez; sadece iade adresleri içindir.
janm

13

Yığınların büyüdüğü yön mimariye özgüdür. Bununla birlikte, benim anlayışıma göre, yalnızca birkaç donanım mimarisinin büyüyen yığınları var.

Bir yığının büyüdüğü yön, tek bir nesnenin düzeninden bağımsızdır. Yani yığın küçülürken, diziler büyümeyecektir (yani & dizi [n] her zaman <& dizi [n + 1] olacaktır);


4

Standartta, işlerin yığın üzerinde nasıl organize edildiğini belirleyen hiçbir şey yoktur. Aslında, sen hiç yığının üzerinde bitişik unsurlar dizi elemanlarını saklamak vermedi uyumlu derleyici inşa hala düzgün dizi öğesi aritmetik yapmak o smarts vardı sağlanan olabilir (bu yüzden bir o, örneğin, biliyordum 1 oldu [0] 'dan 1K uzakta ve bunun için ayarlanabilir).

Farklı sonuçlar almanızın nedeni, yığın "nesneler" eklemek için büyüyebilirken, dizinin tek bir "nesne" olması ve ters sırada artan dizi öğelerine sahip olabilmesidir. Ancak bu davranışa güvenmek güvenli değildir, çünkü yön değişebilir ve değişkenler aşağıdakiler dahil ancak bunlarla sınırlı olmamak üzere çeşitli nedenlerle değiştirilebilir:

  • optimizasyon.
  • hizalama.
  • kişinin kaprisleri derleyicinin yığın yönetimi parçası.

Yığın yönüyle ilgili mükemmel incelemem için buraya bakın :-)

Özel sorularınıza yanıt olarak:

  1. Yığın büyüyor mu azalıyor mu?
    Hiç önemli değil (standart açısından) ama siz sorduğunuz için uygulamaya bağlı olarak bellekte büyüyebilir veya azalabilir.
  2. A [2] ve q bellek adresleri arasında ne olur? Neden orada büyük bir hafıza farkı var? (20 bayt)?
    Hiç önemli değil (standart açısından). Olası nedenler için yukarıya bakın.

Çoğu CPU mimarisinin "büyüme" yöntemini benimsediğini görüyorum, bunu yapmanın herhangi bir avantajı olup olmadığını biliyor musunuz?
baye

Hiçbir fikrim yok, gerçekten. Bu var olası yığın kesişme ihtimalini en aza indirgeyerek, highmem aşağıya doğru gitmeli böylece birisi düşünce kodu 0'dan yukarı gider. Ancak bazı CPU'lar özellikle sıfır olmayan yerlerde kod çalıştırmaya başlar, bu yüzden durum böyle olmayabilir. Çoğu şeyde olduğu gibi, belki de bu, birinin bunu yapmayı düşündüğü ilk yol olduğu için yapıldı :-)
paxdiablo

@lzprgmr: Artan sırayla gerçekleştirilen belirli yığın ayırma türlerine sahip olmanın bazı küçük avantajları vardır ve geçmişte yığın ve yığının ortak bir adresleme alanının zıt uçlarında oturması yaygın olmuştur. Birleşik statik + yığın + yığın kullanımının kullanılabilir belleği aşmaması koşuluyla, bir programın tam olarak ne kadar yığın bellek kullandığı konusunda endişelenmeye gerek yoktu.
supercat

3

Bir x86'da, bir yığın çerçevesinin bellek "tahsisi", yığın işaretçisinden gerekli bayt sayısını çıkarmaktan ibarettir (diğer mimarilerin benzer olduğuna inanıyorum). Bu anlamda, yığının daha derinlemesine çağrıldıkça adresler giderek küçüldüğü için yığın "aşağı" büyüyor sanırım (ama ben her zaman hafızanın sol üstte 0 ile başlayıp siz hareket ettikçe daha büyük adresler alacağını düşünüyorum. sağa ve aşağı kaydırın, böylece zihinsel imajımda yığın büyür ...). Bildirilen değişkenlerin sırasının adresleri üzerinde herhangi bir ilgisi olmayabilir - standardın, yan etkilere neden olmadığı sürece derleyicinin onları yeniden düzenlemesine izin verdiğine inanıyorum (birisi yanlışsam lütfen beni düzeltin) . Onlar'

Dizinin etrafındaki boşluk bir çeşit dolgu olabilir, ama bu benim için gizemli.


1
Aslında, biliyorum o da hiç ayırmaması için ücretsiz çünkü, derleyici olarak yeniden düzenleyebilirsiniz. Onları sadece yazmaçlara koyabilir ve herhangi bir yığın alanı kullanmaz.
rmeador

Adreslerine atıfta bulunursanız onları kayıtlara koyamaz.
florin

iyi nokta, bunu düşünmemişti. ancak derleyicinin bunları yeniden sıralayabileceğinin bir kanıtı olarak yine de yeterli, çünkü en azından bazen bunu yapabileceğini biliyoruz :)
rmeador

1

Her şeyden önce, hafızada 8 baytlık kullanılmayan alan (12 değil, hatırlama yığını aşağı doğru büyür, yani ayrılmayan alan 604'ten 597'ye kadardır). ve neden?. Çünkü her veri türü, boyutuna göre bölünebilen adresten başlayarak bellekte yer kaplar. Bizim durumumuzda 3 tamsayı dizisi 12 bayt bellek alanı alır ve 604, 12'ye bölünemez. Dolayısıyla, 12'ye bölünebilen bir bellek adresiyle karşılaşıncaya kadar boş alanlar bırakır, bu 596'dır.

Yani diziye ayrılan bellek alanı 596'dan 584'e kadardır. Ancak dizi tahsisi devam ettiğinden, dizinin ilk elemanı 596'dan değil 584 adresinden başlar.


1

Derleyici, yerel (otomatik) değişkenleri yerel yığın çerçevesindeki herhangi bir yere tahsis etmekte özgürdür, yığın büyüme yönünü yalnızca bundan güvenilir bir şekilde çıkaramazsınız. Yığın büyüme yönünü, iç içe geçmiş yığın çerçevelerinin adreslerini karşılaştırarak, yani bir işlevin yığın çerçevesindeki yerel bir değişkenin adresini, arananla karşılaştırarak anlayabilirsiniz:


5
İşaretçileri farklı yığın nesnelerine çıkarmanın tanımsız bir davranış olduğundan oldukça eminim - aynı nesnenin parçası olmayan işaretçiler karşılaştırılamaz. Açıkçası, herhangi bir "normal" mimaride çökmeyecek olsa da.
Steve Jessop

@SteveJessop Yığın yönünü programlı olarak elde etmek için bunu düzeltmenin herhangi bir yolu var mı?
xxks-kkk

@ xxks-kkk: ilke olarak hayır, çünkü bir C uygulamasının "yığın yönü" olması gerekmez. Örneğin, bir yığın bloğunun önceden tahsis edildiği bir çağrı kuralına sahip olmak standardı ihlal etmeyecektir ve daha sonra bazı sözde rasgele dahili bellek ayırma yordamı, içinde atlamak için kullanılır. Uygulamada aslında matja'nın tanımladığı gibi çalışıyor.
Steve Jessop

0

Bunun belirleyici olduğunu sanmıyorum. A dizisi "büyüyor" gibi görünüyor çünkü bu bellek bitişik olarak tahsis edilmelidir. Ancak, q ve s birbiriyle hiç ilişkili olmadığından, derleyici her birini yığın içinde keyfi bir boş bellek konumuna yapıştırır, muhtemelen tam sayı boyutuna en iyi uyanlar.

A [2] ile q arasında olan şey, q'nun konumu etrafındaki boşluğun 3 tamsayı dizisini ayıracak kadar büyük olmaması (yani 12 bayttan büyük olmamasıydı).


eğer öyleyse, neden q, s, a olası belleğe sahip değil? (Örn: q adresi: 2293612 s adresi: 2293608 A adresi: 2293604)

S ve a arasında bir "boşluk" görüyorum

Çünkü s ve a birlikte tahsis edilmedi - bitişik olması gereken tek işaretçiler dizideki işaretçilerdir. Diğer bellek her yere tahsis edilebilir.
javanix

0

Yığınım daha düşük numaralı adreslere doğru uzanıyor gibi görünüyor.

Farklı bir derleyici çağrısı kullanırsam, başka bir bilgisayarda veya kendi bilgisayarımda bile farklı olabilir. ... veya derleyici muigt bir yığın kullanmamayı seçer (her şeyi satır içi (her şeyin adresini almadıysam işlevler ve değişkenler)).

$ / usr / bin / gcc -Duvar -Wextra -std = c89 -pedantic stack.c
$ ./a.out
düzey 4: x, 0x7fff7781190c'de
düzey 3: x, 0x7fff778118ec'de
düzey 2: x, 0x7fff778118cc'de
düzey 1: x, 0x7fff778118ac'de
düzey 0: x, 0x7fff7781188c'de

0

Yığın büyür (x86'da). Bununla birlikte, fonksiyon yüklendiğinde yığın bir blokta tahsis edilir ve öğelerin yığında hangi sırayla olacağına dair bir garantiniz yoktur.

Bu durumda, yığın üzerinde iki giriş ve üç int dizisi için yer ayırdı. Ayrıca diziden sonra ek 12 bayt ayırdı, bu nedenle şöyle görünür:

a [12 bayt]
dolgu (?) [12 bayt]
s [4 bayt]
q [4 bayt]

Hangi nedenle olursa olsun, derleyiciniz bu işlev için 32 bayt ve muhtemelen daha fazlasını ayırması gerektiğine karar verdi. Bu bir C programcısı olarak size opak geliyor, nedenini anlayamazsınız.

Nedenini bilmek istiyorsanız, kodu assembly diline derleyin, bunun gcc'de -S ve MS'nin C derleyicisinde / S olduğuna inanıyorum. Bu işlevin açılış talimatlarına bakarsanız, eski yığın işaretçisinin kaydedildiğini ve ardından 32'nin (veya başka bir şeyin!) Çıkarıldığını göreceksiniz. Oradan, kodun bu 32 baytlık bellek bloğuna nasıl eriştiğini görebilir ve derleyicinizin ne yaptığını anlayabilirsiniz. İşlevin sonunda, geri yüklenen yığın işaretçisini görebilirsiniz.


0

İşletim sisteminize ve derleyicinize bağlıdır.


Cevabımın neden reddedildiğini bilmiyorum. Gerçekten işletim sisteminize ve derleyicinize bağlıdır. Bazı sistemlerde yığın aşağı doğru büyürken diğerlerinde yukarı doğru büyür. Ve bazı sistemlerde, gerçek bir aşağı açılan çerçeve yığını yoktur, bunun yerine ayrılmış bir bellek alanı veya kayıt seti ile simüle edilir.
David R Tribble

3
Muhtemelen tek cümlelik iddialar iyi cevaplar olmadığı için.
Orbit'teki Hafiflik Yarışları

0

Yığın büyüyor. Yani f (g (h ())), h için ayrılan yığın daha düşük adreste başlayacak ve sonra g'ler f'den daha düşük olacaktır. Ancak yığın içindeki değişkenler C spesifikasyonuna uymalıdır,

http://c0x.coding-guidelines.com/6.5.8.html

1206 Eğer işaret edilen nesneler aynı toplama nesnesinin üyeleriyse, daha sonra bildirilen yapı üyelerine işaretçiler, yapıda daha önce bildirilen üyelere işaretçilerden daha büyük ve daha büyük alt simge değerlerine sahip dizi öğelerine işaretçiler, aynı nesnenin öğelerine işaretçilerden daha büyük olanları karşılaştırır. alt indis değerlerine sahip dizi.

& a [0] <& a [1], 'a'nın nasıl tahsis edildiğine bakılmaksızın her zaman doğru olmalıdır


Çoğu makinede, yığın yukarı doğru büyüdükleri dışında aşağı doğru büyür.
Jonathan Leffler

0

aşağı doğru büyür ve bunun nedeni bellekteki veri kümesine gelince, küçük endian bayt sırası standardıdır.

Buna bakmanın bir yolu, hafızaya yukarıdan 0'dan ve en alttan maksimumdan bakarsanız yığının yukarı doğru büyümesidir.

Yığının aşağı doğru büyümesinin nedeni, istif veya taban işaretçisinin perspektifinden referanstan kurtulabilmektir.

Herhangi bir türden başvurunun kaldırılmasının en düşükten en yükseğe doğru arttığını unutmayın. Yığın aşağı doğru büyüdüğünden (en yüksekten en düşüğe) bu, yığını dinamik bellek gibi ele almanızı sağlar.

Bu kadar çok programlama ve komut dosyası dilinin kayıt tabanlı değil yığın tabanlı sanal makine kullanmasının bir nedeni budur.


The reason for the stack growing downward is to be able to dereference from the perspective of the stack or base pointer.Çok güzel muhakeme
user3405291

0

Mimariye bağlıdır. Kendi sisteminizi kontrol etmek için GeeksForGeeks'teki bu kodu kullanı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.