Õppetund
Teooria — Kutsepinu: kaadrid, tagastusaadressid ja ületäitumine
Kutsepinu on mälupiirkond, kus iga funktsioonikutse saab oma kaadri. Siin sisaldab see argumenti, tagastusaadressi ehk kohta, kust väljakutsuja jätkab, väljakutsuja salvestatud kaadriviita ja kohalikke muutujaid. Pinuviit näitab väikseimat kasutusel olevat aadressi. Kutse liigutab seda ühe kaadri võrra allapoole, naasmine tagasi ülespoole. Kui rekursiooni sügavus on d kutset ja iga kaader võtab f baiti, kulub pinus d·f baiti.
Mida iga sümbol tähendab
d- sügavus: korraga pinus olevate kaadrite arv
f- ühe kaadri suurus baitides: siin 12, lisaks 4 iga kohaliku muutuja kohta
- Eeldab
- 32-bitine masin klassikalise C kutsekonventsiooniga: 4-baidised pesad, väiksemate aadresside poole kasvav pinu, pinus edastatavad argumendid ja igas kaadris säilitatav kaadriviit. Tegelikud 64-bitised kutsekonventsioonid edastavad esimesed argumendid registrites ja kompilaatorid võivad kaadriviida ära jätta. ARM-il võib funktsioon, mis ise midagi välja ei kutsu, hoida tagastusaadressi registris. Seetõttu erinevad tegelike kaadrite suurus ja paigutus siin näidatud kolmest pesast.
Ülesanne täielikult lahendatud
-
Kui sügavale saab rekursioon minna? 4 sammu
Vajuta nuppu fact(11): ületäitumine. Selle lehe pinu mahutab 128 baiti. Microsofti linkur eraldab programmile vaikimisi 1 MB pinu. Võtame selleks 1 MiB ehk 1 048 576 baiti ja kasutame siinseid kaadreid. Kui sügavale saab fact minna? Kui sügavale siis, kui igal kutsel on ka üks 4-baidine kohalik muutuja? Kui palju pinu vajab fib(30) ja mitu kutset see teeb?
-
Siinne kaader koosneb argumendist, tagastusaadressist ja salvestatud kaadriviidast: 3 × 4 = 12 baiti. 128 / 12 = 10,67, seega mahub pinusse 10 kaadrit. fact(11) vajab 11 × 12 = 132 baiti. Tulemuste tabel näitab ületäitumist.
-
1 048 576 / 12 = 87 381,33, seega mahub pinusse 87 381 kaadrit. fact saaks jõuda 87 381 rekursioonitasemeni, enne kui pinuruum otsa lõpeb. Vastus ületab 32-bitise int-tüübi piiri juba palju varem: 13! = 6 227 020 800.
-
Ühe 4-baidise kohaliku muutujaga võtab iga kaader 16 baiti ning 1 048 576 / 16 = 65 536 täpselt.
-
fib(30) pikimas ahelas on 30 kaadrit: 30 × 12 = 360 baiti. Kutsete arv on 2·F(31) − 1, kus F(31) = 1 346 269 on 31. Fibonacci arv. Kokku tehakse 2 692 537 kutset.
Vastus
87 381 taset või ühe kohaliku muutujaga 65 536. fib(30) vajab 2 692 537 kutse tegemiseks 360 baiti pinu. Sügavus määrab pinukasutuse; fib-i puhul määrab kutsete arv ajakulu.
-
Õpitee
Kuidas arvuti programmi täidab
Allikad (2)
- The worked problem's stack size: the linker's default reservation: Microsoft Learn, "/STACK (Stack allocations)," C/C++ build reference, updated 23 February 2022.
- The first insight's 1988 example: fingerd's unbounded read into a 512-byte buffer on the stack: Bob Page, "A Report on the Internet Worm." University of Lowell, 7 November 1988 (copy hosted at Toronto Metropolitan University).