Memahami stack frame dari panggilan fungsi di C / C ++?

19

Saya mencoba memahami bagaimana susunan bingkai dibangun dan variabel (params) mana yang didorong untuk ditumpuk dalam urutan apa? Beberapa hasil pencarian menunjukkan bahwa kompiler C / C ++ memutuskan berdasarkan operasi yang dilakukan dalam suatu fungsi. Misalnya, jika fungsi seharusnya hanya menambah nilai int pass-in oleh 1 (mirip dengan operator ++) dan mengembalikannya, itu akan menempatkan semua parameter fungsi dan variabel lokal ke dalam register.

Saya ingin tahu register mana yang digunakan untuk mengembalikan atau melewati parameter nilai. Bagaimana referensi dikembalikan? Bagaimana kompiler memilih antara eax, ebx, ecx dan edx?

Apa yang perlu saya ketahui untuk memahami bagaimana register, tumpukan dan tumpukan referensi digunakan, dibangun dan dihancurkan selama pemanggilan fungsi?

Gana
sumber
ini agak sulit dibaca (dinding teks). Maukah Anda mengedit posting Anda menjadi bentuk yang lebih baik?
nyamuk
1
Pertanyaan ini agak luas bagi saya. Juga, bukankah ini akan sangat spesifik platform?
Kazark
Pertanyaan juga ditanyakan pada SO: stackoverflow.com/questions/16088040/…
Wayne Conrad
Lihat juga jawaban saya pada SO
Basile Starynkevitch

Jawaban:

11

Selain apa yang Dirk katakan, penggunaan penting dari frame tumpukan adalah untuk menyimpan nilai register sebelumnya sehingga mereka dapat dikembalikan setelah panggilan fungsi. Jadi, bahkan pada prosesor di mana register digunakan untuk melewati parameter, mengembalikan nilai, dan menyimpan alamat kembali, nilai register tersebut disimpan di stack sebelum panggilan fungsi sehingga mereka dapat dikembalikan setelah panggilan. Ini memungkinkan satu fungsi untuk memanggil yang lain tanpa menimpa parameternya sendiri atau lupa alamat pengirimnya sendiri.

Jadi, memanggil fungsi B dari fungsi A pada sistem "generik" yang khas mungkin melibatkan langkah-langkah berikut:

  • fungsi A:
    • dorong ruang untuk nilai kembali
    • parameter dorong
    • dorong alamat pengirim
  • lompat ke fungsi B
  • fungsi B:
    • dorong alamat frame stack sebelumnya
    • nilai push register yang menggunakan fungsi ini (sehingga mereka dapat dikembalikan)
    • ruang dorong untuk variabel lokal
    • lakukan perhitungan yang diperlukan
    • pulihkan register
    • mengembalikan bingkai tumpukan sebelumnya
    • menyimpan hasil fungsi
    • lompat ke alamat pengirim
  • fungsi A:
    • pop parameter
    • pop nilai pengembalian

Ini sama sekali bukan satu-satunya cara pemanggilan fungsi dapat bekerja (dan saya mungkin memiliki satu atau dua langkah keluar dari urutan), tetapi seharusnya memberi Anda gambaran tentang bagaimana stack digunakan untuk membiarkan prosesor menangani panggilan fungsi yang disarangkan.

Caleb
sumber
Apa arti "push" di sini? Saya tidak tahu harus bagaimana.
Tomáš Zato - Reinstate Monica
2
@ TomášZato pushdan popmerupakan dua operasi mendasar pada stack. Tumpukan adalah struktur masuk terakhir keluar pertama, seperti tumpukan buku. Saat Anda push, Anda meletakkan objek baru di atas tumpukan; saat Anda popmengambil objek dari atas tumpukan. Anda tidak diizinkan untuk menyisipkan atau menghapus objek di tengah, Anda hanya dapat beroperasi di atas tumpukan. Anda mungkin membaca lebih lanjut tentang tumpukan secara umum dan tumpukan program khususnya di Wikipedia.
Caleb
11

Ini tergantung pada konvensi panggilan yang digunakan. Siapa pun yang mendefinisikan konvensi pemanggilan dapat membuat keputusan ini bagaimanapun yang mereka inginkan.

Dalam konvensi panggilan paling umum pada x86, register tidak digunakan untuk melewatkan parameter; parameter didorong ke tumpukan dimulai dengan parameter paling kanan. Nilai kembali ditempatkan di eax dan dapat menggunakan edx jika membutuhkan ruang ekstra. Referensi dan pointer keduanya dikembalikan dalam bentuk alamat di eax.

Dirk Holsopple
sumber
5

Jika Anda memahami tumpukan dengan sangat baik maka Anda akan memahami bagaimana memori bekerja dalam program dan jika Anda memahami bagaimana memori bekerja dalam program, Anda akan memahami bagaimana fungsi menyimpan dalam program dan jika Anda memahami bagaimana fungsi menyimpan dalam program Anda akan memahami bagaimana fungsi rekursif bekerja dan jika Anda memahami bagaimana fungsi rekursif bekerja, Anda akan memahami cara kerja kompiler dan jika Anda memahami cara kerja kompiler, pikiran Anda akan berfungsi sebagai kompiler dan Anda akan men-debug program apa pun dengan sangat mudah

Biarkan saya menjelaskan cara kerja tumpukan:

Pertama, Anda harus tahu bagaimana fungsi menyimpan di stack:

Heap menyimpan nilai alokasi memori dinamis. Nilai alokasi dan penghapusan otomatis tumpukan disimpan.

masukkan deskripsi gambar di sini

Mari kita pahami dengan contoh:

def hello(x):
    if x==1:
        return "op"
    else:
        u=1
        e=12
        s=hello(x-1)
        e+=1
        print(s)
        print(x)
        u+=1
    return e

hello(4)

Sekarang pahami bagian dari program ini:

masukkan deskripsi gambar di sini

Sekarang mari kita lihat apa itu stack dan apa itu stack parts:

masukkan deskripsi gambar di sini

Alokasi tumpukan:

Ingat satu hal jika ada fungsi mendapatkan "kembali" tidak peduli itu telah memuat semua variabel lokalnya atau apa pun itu akan segera kembali dari tumpukan akan bingkai tumpukannya. Ini berarti ketika setiap fungsi rekursif mendapatkan kondisi basis dan kami mengembalikannya setelah kondisi dasar sehingga kondisi basis tidak akan menunggu untuk memuat variabel lokal yang terletak di bagian "lain" dari program, ia akan segera mengembalikan frame saat ini dari stack dan sekarang jika satu frame kembali frame berikutnya dalam catatan aktivasi. Lihat ini secara praktis:

masukkan deskripsi gambar di sini

Deallokasi blok:

Jadi sekarang setiap kali fungsi ditemukan pernyataan kembali itu menghapus bingkai saat ini dari tumpukan.

saat kembali dari nilai tumpukan akan kembali dalam urutan terbalik di mana mereka dialokasikan dalam tumpukan.

masukkan deskripsi gambar di sini

Ini adalah deskripsi yang sangat singkat dan jika Anda ingin tahu lebih dalam tentang stack dan double recursion baca dua posting blog ini:

Lebih lanjut tentang tumpukan langkah demi langkah

Lebih lanjut tentang rekursi ganda langkah demi langkah dengan tumpukan

pengguna5904928
sumber
3

Apa yang Anda cari disebut Application Binary Interface - ABI.

Ada spesifikasi untuk setiap kompiler yang merinci ABI.

Setiap platform biasanya akan menentukan dan ABI untuk mendukung interoperabilitas antar kompiler. Sebagai contoh, Konvensi Memanggil x86 merinci konvensi pemanggilan khas untuk x86 dan x86-64. Saya mengharapkan dokumen yang lebih resmi daripada wikipedia.

Bill Door
sumber