ว่าด้วยเรื่องหน่วยความจำ สิ่งที่หลายคนมองข้าม : Stack


      ก็มาถึงกับบทความตอนที่ 3 กันแล้วว ในบทความนี้เจ้าของบล็อกก็จะขอขึ้นต้นเกี่ยวกับเรื่องของ Stack เป็นเรื่องที่เราติดค้างกันไว้ในบทความก่อนหน้านี้


      ว่าด้วยเรื่อง Stack memory มันก็คือหน่วยความจำที่เป็นส่วนหนึ่งใน SRAM ที่เจ้าของบล็อกได้อธิบายไปในบทความ ว่าด้วยเรื่องหน่วยความจำ สิ่งที่หลายคนมองข้าม ตอนที่ 1 ซึ่งก่อนหน้านี้เจ้าของบล็อกได้บอกไว้ว่า ในส่วนนี้ไม่ค่อยจำเป็นที่เราจะต้องกังวลมาก แต่ก็ไม่ใช่ว่าจะไม่มีเรื่องให้ต้องกังวลเลย เพราะฉะนั้นเราก็มาดูกันว่า Stack มันทำงานกันอย่างไร ในบทความนี้ขอยกความดีความชอบให้กับเจ้า Stack ทั้งบทความเลยแล้วกัน ฮ่า ๆ




      เรามาทบทวนความจำกับรูปนี้กันก่อนดีกว่า ตอนนี้เรากำลังพูดถึงเรื่องของ Stack กันนะครับ ซึ่งส่วนของ stack memory จะอยู่ด้านบนสุดของ memory นั่นเอง จะเห็นว่าลูกศรมันชี้ลงมา เจ้าของบล็อกก็เลยอยากจะบอกว่า เวลาจองหน่วยความจำใน Stack มันเป็นแบบนั้นจริง ๆ จะเริ่มจาก address ที่อยู่สูง ลงไปหา address ที่อยู่ข้างล่าง

     การใช้งานใน stack memory เป็นสิ่งที่ใกล้ตัวเรามาก บางคนอาจจะยังไม่รู้ว่านี่มันอยู่ในส่วนของ stack memory หรอ เพราะจริง ๆ แล้วมันก็คือ local variable หรือตัวแปรทั่วไปที่อยู่ใน block หรือ {...} (ปีกกา) แล้วก็พวก function call ทั้งหลายแหล่ เดี๋ยวลองทดสอบ code ตามด้านล่าง



     ลองสังเกตุผลลัพธ์ที่ตามมาสิ



     
     code ตัวอย่างเบื้องต้นคือพยายามสร้างตัวแปรขึ้นมา เป็นตัวแปรแบบ Local ทั้งใน function main แล้วก็ function ย่อยทั้ง 2 แล้วก็ดูว่า address ของตัวแปรที่ถูกจองไว้มีพฤติกรรมยังไง จะเห็นว่าค่า address เริ่มจากมากสุดน้อยลงมาเรื่อย ๆ

Call stack
     ก่อนหน้านี้มันคือการเกริ่นเกี่ยวกับ Stack แบบ คร่าว ๆ แต่ว่าต่อไปนี้ จะเริ่มเข้าเนื้อหาของ Stack กัน

     ขอยกตัวอย่างจาก code ก่อนหน้านี้นะ แต่จะเพิ่ม code ในส่วนของ main บรรทัดนึง ก็คือเพิ่ม foo1() เข้าไป




     แล้วเรามาดูการทำงานของ Stack กัน


     จากรูปจะเห็นว่าเมื่อเรา call function ที่ main มันจะมี stack โผล่ขึ้นมาอีกก้อนนึง ซึ่งภายในนั้นก็จะมี local variable อยู่ในนั้นอีกที แต่ยังไม่หมดแค่นี้ ใน call function นั้นก็ยัง call function เพิ่มขึ้นไปอีก 1 function มันก็เลยกลายเป็นว่าเพิ่ม stack ไปอีกก้อนนึง

     การทำงานจะเริ่มจาก Top stack ก่อน หรือก็คือ stack ก้อนบนสุด ในที่นี้ก็คือ block สีแดง เมื่อ function foo1 ทำงานเสร็จแล้ว มันจะทำลายตัวเอง ตัวแปรที่เคยประกาศใน function นี้ ก็จะตายตามไปด้วย ซึ่งใน function foo หรือ block สีฟ้า ก็จะมีลักษณะเหมือนกันกับสีแดงเลย สุดท้ายที่ main ก็จะเรียก block สีแดงขึ้นมา จากนั้นเมื่อมันทำงานเสร็จก็จะทำลายตัวเอง เช่นเดียวกันถ้าโปรแกรมจบทำงาน function main ก็จะถูกทำลาย


รูปภาพจาก https://en.wikipedia.org/wiki/Call_stack

     ทีนี้เมื่อเรามาเจาะลงลึกเข้าไปอีกในการทำงานของ call stack ตามตัวอย่างรูปภาพ block สีฟ้าสมมติว่ามันคือ function ที่ถูกเรียกจาก main ก็แล้วกัน ในเบื้องต้นมันจะเริ่มจาก parameters ที่ส่งมาให้ function นี้ก่อน ในลำดับถัดมาคือการระบุ address ของ return ที่ function จะ return กลับมา แล้วค่อยเป็นส่วนของ local
     เมื่อมีการ call function ไปอีก ก็จะเหมือน block สีเขียว คือจะต้องส่ง parameter จาก block สีฟ้าไปให้ จากนั้น block สีเขียวก็จะระบุ address ของ return แล้วค่อยมาทำงานในส่วนของ local เมื่อทำงานส่วน local เสร็จ block จากด้านบนจะค่อย ๆ ถูกลบออกจนไม่เหลือ block อะไรเลย


ปัญหาจาก Stack

     

     อย่างที่บอกว่ามันก็ยังต้องมีบ้างแหละเรื่องที่กังวล แล้วอะไรที่ต้องกังวลกัน? เจ้าของบล็อกก็อยากจะบอกว่า มันยังมีเรื่อง stack overflow อยู่อีกนะ เพราะบางทีมันจะเป็นในรูปแบบของ function ที่เรียกใช้งานตัวเองหรือ Recursive function ความหมายของ stack overflow ในที่นี้ก็คือการจองหน่วยความจำเกินขนาดของมันที่มันจะจองได้ มันจึงเกิด overflow ขึ้น


เรามาดูกันดีกว่าว่าเขียนแบบไหนทำให้เกิด overflow ได้บ้าง



Infinite recursion

     Infinite recursion หรือก็คือการเรียกซ้ำแบบอนันต์ เป็นสาเหตุที่ค่อนข้างเจอบ่อยที่สุดที่ทำให้เกิด overflow เพราะเกิดจากการที่ function เรียกตัวเองซ้ำไปเรื่อย ๆ จนมันเกินขนาดที่ Stack จะเก็บได้ จนเกิดเป็น overflow




Very deep recursion


     Very deep recursion หรือก็คือการเรียกใช้งาน function ซ้ำที่มีความลึกมาก ๆ ถึงสาเหตุนี้จะไม่หนักเท่ากับปัญหาแรก แต่ว่าการเรียก function ที่ลึกมาก ๆ ก็อาจจะเป็นสาเหตุที่ทำให้เกิด stack overflow ได้เช่นเดียวกัน


     อันนี้ยกตัวอย่างการเขียนโปรแกรมหาค่า fibonacci โดยใช้ recursive function ถ้าหากว่าเราใส่ค่าที่มาก ๆ มันก็อาจจะทำให้เกิด stack overflow ได้นะฮะ


Very large stack variables


     Very large stack variables หรือก็คือการประกาศตัวแปรที่มีขนาดใหญ่มาก ๆ เกินขอบเขตของ Stack จนทำให้เกิด Overflow

     คร่าว ๆ ก็เอาเท่านี้ก่อนเนาะ จริง ๆ ยังเหลือเนื้อหาอีก แต่กลัวว่าบทความนี้จะยาวเกินไป เพราะจริง ๆ ยังมีเรื่อง stack buffer overflow  อีก ก็ลองไปศึกษาเพิ่มเติมเอาเนาะ ไม่เยอะหรอก

     ยังไงก็ยังเหลือค้างพวก EEPROM อีก ก็ดูกันอีกว่าจะมีเวลาเขียนตอนไหน แต่คงไม่ให้นานเหมือนครั้งนี้แน่นอน ยังไงก็ช่วย Feedback ด้วยนะครับแลกเปลี่ยนแชร์กัน เผื่อเจ้าของบล็อกจะได้ปรับปรุงเนื้อหาให้ดียิ่งขึ้น :)

อ่านบทความตอนต่อไปได้ที่ ว่าด้วยเรื่องหน่วยความจำ สิ่งที่หลายคนมองข้าม ตอนที่ 4

reference : https://en.wikipedia.org/wiki/Call_stack , Memory of an arduino 

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

ว่าด้วยเรื่องหน่วยความจำ สิ่งที่หลายคนมองข้าม : รู้จักกับ Memory

คณิตศาสตร์กับโปรแกรมมิ่งมันเป็นยังไงนะ ตอนที่ 1 เกริ่นพีทาโกรัส

Pointer กับตัวแปร Array นะจ๊ะ.. [Back to basic แต่ไม่ basic]