Tumpukan memungkinkan kita untuk memotong batas yang diberlakukan oleh jumlah register yang terbatas secara elegan.
Bayangkan memiliki 26 global "register az" (atau bahkan hanya memiliki register berukuran 7 byte dari chip 8080) Dan setiap fungsi yang Anda tulis dalam aplikasi ini berbagi daftar datar ini.
Awal yang naif adalah mengalokasikan beberapa register pertama ke fungsi pertama, dan mengetahui bahwa hanya butuh 3, mulai dengan "d" untuk fungsi kedua ... Anda kehabisan dengan cepat.
Sebaliknya, jika Anda memiliki pita metaforis, seperti mesin turing, Anda dapat meminta setiap fungsi memulai "panggil fungsi lain" dengan menyimpan semua variabel yang digunakan dan meneruskan () rekaman itu, dan kemudian fungsi callee dapat mengacaukan sebanyak mungkin mendaftar sesuai keinginan. Ketika callee selesai, ia mengembalikan kontrol ke fungsi induknya, yang tahu di mana harus mengambil output callee sesuai kebutuhan, dan kemudian memutar kaset mundur untuk mengembalikan kondisinya.
Frame panggilan dasar Anda adalah hanya itu, dan dibuat dan dihapus oleh urutan kode mesin standar yang dimasukkan oleh kompiler di sekitar transisi dari satu fungsi ke fungsi lainnya. (Sudah lama sejak saya harus mengingat frame stack C saya, tetapi Anda dapat membaca tentang berbagai cara tugas siapa yang menjatuhkan apa yang ada di X86_calling_conventions .)
(rekursi itu luar biasa, tetapi jika Anda harus menyulap register tanpa tumpukan, maka Anda akan sangat menghargai tumpukan.)
Saya kira peningkatan ruang hard disk dan RAM yang diperlukan untuk menyimpan program dan mendukung kompilasi (masing-masing) adalah alasan mengapa kami menggunakan tumpukan panggilan. Apakah itu benar?
Meskipun kita dapat menyejajarkan lebih banyak hari-hari ini, ("lebih banyak kecepatan" selalu baik; "lebih sedikit kb rakitan" berarti sangat sedikit dalam dunia aliran video) Batasan utama adalah kemampuan kompiler untuk meratakan jenis pola kode tertentu.
Misalnya, objek polimorfik - jika Anda tidak tahu satu-satunya jenis objek yang akan Anda berikan, Anda tidak dapat meratakannya; Anda harus melihat vtable objek dari fitur dan memanggil melalui pointer itu ... sepele untuk dilakukan saat runtime, tidak mungkin untuk inline pada waktu kompilasi.
Toolchain modern dapat dengan senang hati menggarisbawahi fungsi yang terdefinisi secara polimorfik ketika ia telah meratakan cukup banyak pemanggil untuk mengetahui dengan pasti aroma obj yang mana:
class Base {
public: void act() = 0;
};
class Child1: public Base {
public: void act() {};
};
void ActOn(Base* something) {
something->act();
}
void InlineMe() {
Child1 thingamabob;
ActOn(&thingamabob);
}
di atas, kompiler dapat memilih untuk tetap menggunakan inline statis, dari InlineMe melalui tindakan apa pun yang ada di dalam (), atau kebutuhan untuk menyentuh vtable apa pun saat runtime.
Tetapi setiap ketidakpastian dalam apa rasa objek akan meninggalkan sebagai panggilan ke fungsi diskrit, bahkan jika beberapa doa lain dari fungsi yang sama yang inline.