ทำความเข้าใจกับกรอบสแต็คของการเรียกใช้ฟังก์ชันใน C / C ++ หรือไม่


19

ฉันกำลังพยายามที่จะเข้าใจว่าสแต็คเฟรมถูกสร้างขึ้นและตัวแปรใด (params) ถูกผลักไปยังสแต็กตามลำดับ ผลลัพธ์การค้นหาบางรายการแสดงว่าคอมไพเลอร์ C / C ++ ตัดสินใจตามการดำเนินการที่ดำเนินการภายในฟังก์ชั่น ตัวอย่างเช่นถ้าฟังก์ชั่นควรจะเพิ่มค่าการส่งผ่านโดย 1 (คล้ายกับตัวดำเนินการ ++) และส่งกลับมันจะทำให้พารามิเตอร์ทั้งหมดของฟังก์ชั่นและตัวแปรท้องถิ่นในการลงทะเบียน

ฉันสงสัยว่าจะใช้รีจิสเตอร์ใดสำหรับพารามิเตอร์ส่งคืนหรือส่งผ่านตามค่า การอ้างอิงถูกส่งคืนอย่างไร คอมไพเลอร์เลือกระหว่าง eax, ebx, ecx และ edx อย่างไร

ฉันต้องทราบอะไรบ้างในการทำความเข้าใจวิธีการลงทะเบียนการอ้างอิงสแต็คและฮีปที่ใช้สร้างและทำลายระหว่างการเรียกใช้ฟังก์ชัน


นี่ค่อนข้างอ่านยาก (ผนังข้อความ) คุณต้องการแก้ไขการโพสต์ของคุณให้มีรูปร่างที่ดีขึ้นหรือไม่
ริ้น

1
คำถามนี้ดูเหมือนจะค่อนข้างกว้างสำหรับฉัน นอกจากนี้สิ่งนี้จะไม่เจาะจงแพลตฟอร์มหรือไม่
Kazark

คำถามถูกถามใน SO: stackoverflow.com/questions/16088040/…
Wayne Conrad

คำตอบ:


11

นอกจากสิ่งที่เดิร์กกล่าวแล้วการใช้สแต็กเฟรมที่สำคัญคือการบันทึกค่าก่อนหน้าของรีจิสเตอร์เพื่อให้สามารถเรียกคืนได้หลังจากการเรียกใช้ฟังก์ชัน ดังนั้นแม้บนตัวประมวลผลที่รีจิสเตอร์ใช้สำหรับส่งพารามิเตอร์ส่งคืนค่าและบันทึกที่อยู่ส่งคืนค่าของรีจิสเตอร์เหล่านั้นจะถูกบันทึกไว้ในสแต็กก่อนการเรียกฟังก์ชันเพื่อให้สามารถเรียกคืนได้หลังจากการโทร สิ่งนี้ทำให้ฟังก์ชั่นหนึ่งสามารถเรียกใช้ฟังก์ชันอื่นได้โดยไม่ต้องเขียนทับพารามิเตอร์ของตัวเองหรือลืมที่อยู่ผู้ส่งคืน

ดังนั้นการเรียกใช้ฟังก์ชัน B จากฟังก์ชัน A บนระบบ "ทั่วไป" ทั่วไปอาจเกี่ยวข้องกับขั้นตอนต่อไปนี้:

  • ฟังก์ชั่น A:
    • push space สำหรับค่าส่งคืน
    • พารามิเตอร์การผลักดัน
    • ดันที่อยู่ผู้ส่ง
  • ข้ามไปยังฟังก์ชัน B
  • ฟังก์ชั่น B:
    • ดันแอดเดรสของเฟรมสแต็กก่อนหน้า
    • กดค่าการลงทะเบียนที่ฟังก์ชันนี้ใช้ (เพื่อให้สามารถเรียกคืนได้)
    • พื้นที่ผลักดันสำหรับตัวแปรท้องถิ่น
    • ทำการคำนวณที่จำเป็น
    • คืนค่าการลงทะเบียน
    • เรียกคืนเฟรมสแต็กก่อนหน้า
    • เก็บผลลัพธ์ของฟังก์ชัน
    • ข้ามไปยังที่อยู่ผู้ส่ง
  • ฟังก์ชั่น A:
    • ป๊อปอัพพารามิเตอร์
    • ป๊อปอัพค่าตอบแทน

นี่ไม่ใช่วิธีเดียวที่การเรียกใช้ฟังก์ชั่นทางเดียวสามารถทำงานได้ (และฉันอาจมีขั้นตอนหนึ่งหรือสองอย่างผิดปกติ) แต่ควรให้คุณทราบว่าจะใช้สแต็กเพื่อให้ตัวประมวลผลจัดการกับการเรียกฟังก์ชันที่ซ้อนกันได้อย่างไร


"พุช" หมายถึงอะไรที่นี่? ฉันไม่รู้ว่าจะทำยังไง
Tomáš Zato - Reinstate Monica

2
@ TomášZato pushและpopเป็นปฏิบัติการพื้นฐานสองอย่างในสแต็ก สแต็คเป็นโครงสร้างสุดท้ายเข้าก่อนออกเช่นสแต็กของหนังสือ เมื่อคุณpushวางวัตถุใหม่ที่ด้านบนของสแต็ก เมื่อคุณpopใช้วัตถุจากด้านบนของสแต็ค คุณไม่ได้รับอนุญาตให้แทรกหรือลบวัตถุที่อยู่ตรงกลางคุณสามารถทำงานที่ด้านบนสุดของสแต็กเท่านั้น คุณอาจอ่านเพิ่มเติมเกี่ยวกับสแต็คโดยทั่วไปและโปรแกรมสแต็กโดยเฉพาะใน Wikipedia
Caleb

11

ขึ้นอยู่กับระเบียบการโทรที่ใช้ ใครก็ตามที่กำหนดอนุสัญญาเรียกสามารถตัดสินใจได้อย่างไรก็ตามพวกเขาต้องการ

ในแบบแผนการโทรทั่วไปใน x86 การลงทะเบียนจะไม่ถูกใช้สำหรับการส่งพารามิเตอร์ พารามิเตอร์จะถูกส่งไปยังสแต็กโดยเริ่มต้นด้วยพารามิเตอร์ขวาสุด ค่าส่งคืนจะถูกวางใน eax และสามารถใช้ edx หากต้องการพื้นที่เพิ่มเติม การอ้างอิงและตัวชี้จะถูกส่งคืนในรูปแบบของที่อยู่ใน eax


5

ถ้าคุณเข้าใจ stack ได้ดีแล้วคุณจะเข้าใจว่าหน่วยความจำทำงานอย่างไรในโปรแกรมและถ้าคุณเข้าใจว่าหน่วยความจำทำงานอย่างไรในโปรแกรมคุณจะเข้าใจว่า function store ในโปรแกรมและถ้าคุณเข้าใจว่า function store ในโปรแกรมคุณจะเข้าใจว่า คุณเข้าใจว่าฟังก์ชั่น recursive ทำงานอย่างไรคุณจะเข้าใจว่าคอมไพเลอร์ทำงานอย่างไรและถ้าคุณเข้าใจว่าคอมไพเลอร์ทำงานอย่างไรใจของคุณจะทำงานเป็นคอมไพเลอร์และคุณจะดีบักโปรแกรมใด ๆ ได้อย่างง่ายดาย

ให้ฉันอธิบายว่าสแต็คทำงานอย่างไร:

ก่อนอื่นคุณต้องรู้ว่าฟังก์ชั่นจัดเก็บในกองซ้อนได้อย่างไร:

กองการจัดเก็บค่าการจัดสรรหน่วยความจำแบบไดนามิก การจัดเก็บอัตโนมัติสแต็กการปันส่วนและค่าลบ

ป้อนคำอธิบายรูปภาพที่นี่

มาทำความเข้าใจกับตัวอย่าง:

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)

ตอนนี้เข้าใจบางส่วนของโปรแกรมนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ทีนี้มาดูสแต็คคืออะไรและอะไรคือสแต็กชิ้นส่วน:

ป้อนคำอธิบายรูปภาพที่นี่

การจัดสรรสแต็ก:

จำสิ่งหนึ่งถ้าฟังก์ชั่นใด ๆ ที่ได้รับ“ คืน” ไม่ว่าจะโหลด varibles ในตัวของเขาทั้งหมดหรืออะไรก็ตามที่มันจะส่งคืนจากสแต็กจะเป็นเฟรมสแต็กของเขาทันที มันหมายความว่าเมื่อฟังก์ชั่นแบบเรียกซ้ำได้รับสภาพฐานและเราใส่คืนค่าหลังจากสภาพพื้นฐานดังนั้นสภาพฐานจะไม่รอโหลดตัวแปรท้องถิ่นซึ่งตั้งอยู่ในส่วน "อื่น ๆ " ของโปรแกรมมันจะส่งคืนเฟรมปัจจุบันทันทีจากสแต็ค return frame ถัดไปอยู่ในบันทึกการเปิดใช้งาน ดูสิ่งนี้ในทางปฏิบัติ:

ป้อนคำอธิบายรูปภาพที่นี่

การยกเลิกการจัดสรรบล็อก:

ดังนั้นเมื่อใดก็ตามที่ฟังก์ชั่นพบคำสั่ง return มันจะลบเฟรมปัจจุบันออกจากสแต็ก

ในขณะที่ส่งคืนจากค่าสแต็กจะส่งกลับในลำดับย้อนกลับของลำดับที่จัดสรรในสแต็ก

ป้อนคำอธิบายรูปภาพที่นี่

เหล่านี้เป็นคำอธิบายสั้น ๆ และหากคุณต้องการทราบรายละเอียดเพิ่มเติมเกี่ยวกับกองซ้อนและการเรียกซ้ำสองครั้งอ่านโพสต์สองบล็อกนี้:

เพิ่มเติมเกี่ยวกับสแต็คทีละขั้นตอน

เพิ่มเติมเกี่ยวกับการเรียกซ้ำสองครั้งทีละขั้นตอนด้วยสแต็ก


3

สิ่งที่คุณกำลังมองหาเรียกว่าApplication Binary Interface - ABI

มีข้อกำหนดสำหรับคอมไพเลอร์แต่ละรายการที่สะกดคาถา ABI

แต่ละแพลตฟอร์มมักจะระบุและ ABI เพื่อสนับสนุนการทำงานร่วมกันระหว่างคอมไพเลอร์ ตัวอย่างเช่นx86 Calling Conventions จะสะกดคำเรียกการประชุมทั่วไปสำหรับ x86 และ x86-64 ฉันคาดหวังว่าจะมีเอกสารอย่างเป็นทางการมากกว่าวิกิพีเดีย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.