เมื่อคอมพิวเตอร์จัดเก็บตัวแปรเมื่อโปรแกรมต้องการรับค่าของตัวแปรคอมพิวเตอร์จะทราบได้อย่างไรว่าจะค้นหาหน่วยความจำสำหรับค่าของตัวแปรนั้นได้อย่างไร
เมื่อคอมพิวเตอร์จัดเก็บตัวแปรเมื่อโปรแกรมต้องการรับค่าของตัวแปรคอมพิวเตอร์จะทราบได้อย่างไรว่าจะค้นหาหน่วยความจำสำหรับค่าของตัวแปรนั้นได้อย่างไร
คำตอบ:
ฉันขอแนะนำให้คุณดูในโลกมหัศจรรย์ของการก่อสร้างคอมไพเลอร์! คำตอบคือมันเป็นกระบวนการที่ค่อนข้างซับซ้อน
ในการพยายามให้สัญชาตญาณคุณจำไว้ว่าชื่อตัวแปรล้วนอยู่ที่นั่นเพื่อประโยชน์ของโปรแกรมเมอร์ คอมพิวเตอร์จะเปลี่ยนทุกอย่างให้เป็นที่อยู่ท้ายที่สุด
ตัวแปรโลคัลคือ (โดยทั่วไป) เก็บไว้ในสแต็ก: นั่นคือมันเป็นส่วนหนึ่งของโครงสร้างข้อมูลที่แสดงถึงการเรียกใช้ฟังก์ชัน เราสามารถกำหนดรายการตัวแปรทั้งหมดที่ฟังก์ชั่นใช้ (อาจ) ใช้โดยดูจากฟังก์ชั่นนั้นดังนั้นคอมไพเลอร์สามารถดูจำนวนตัวแปรที่ต้องการสำหรับฟังก์ชั่นนี้และจำนวนพื้นที่ที่แต่ละตัวแปรใช้
มีเวทมนตร์เล็กน้อยที่เรียกว่าตัวชี้สแต็กซึ่งเป็นรีจิสเตอร์ที่เก็บที่อยู่ของสแต็กปัจจุบันที่เริ่มต้นเสมอ
ตัวแปรแต่ละตัวจะได้รับ "ออฟเซ็ตสแต็ก" ซึ่งเป็นตำแหน่งที่เก็บสแต็ก จากนั้นเมื่อโปรแกรมต้องการเข้าถึงตัวแปรx
คอมไพเลอร์จะแทนที่x
ด้วยSTACK_POINTER + x_offset
เพื่อให้ได้สถานที่จริงที่เก็บไว้ในหน่วยความจำ
โปรดทราบว่านี่คือสาเหตุที่คุณได้รับพอยน์เตอร์กลับมาเมื่อคุณใช้malloc
หรือnew
ใน C หรือ C ++ คุณไม่สามารถระบุได้ว่าค่าที่จัดสรรฮีปอยู่ในหน่วยความจำอย่างแน่นอนดังนั้นคุณต้องเก็บตัวชี้ไว้ ตัวชี้นั้นจะอยู่บนสแต็ก แต่จะชี้ไปที่ฮีป
รายละเอียดของการอัปเดตสแต็คสำหรับการเรียกใช้ฟังก์ชันและการส่งคืนนั้นซับซ้อนดังนั้นฉันขอแนะนำThe Dragon BookหรือThe Tiger Bookหากคุณสนใจ
เมื่อคอมพิวเตอร์จัดเก็บตัวแปรเมื่อโปรแกรมต้องการรับค่าของตัวแปรคอมพิวเตอร์จะทราบได้อย่างไรว่าจะค้นหาหน่วยความจำสำหรับค่าของตัวแปรนั้นได้อย่างไร
โปรแกรมบอกมัน คอมพิวเตอร์ไม่มีแนวคิดเรื่อง "ตัวแปร" ในตัว - เป็นภาษาระดับสูงอย่างสิ้นเชิง!
นี่คือโปรแกรม C:
int main(void)
{
int a = 1;
return a + 3;
}
และนี่คือรหัสแอสเซมบลีที่คอมไพล์เป็น: (ความคิดเห็นที่ขึ้นต้นด้วย;
)
main:
; {
pushq %rbp
movq %rsp, %rbp
; int a = 1
movl $1, -4(%rbp)
; return a + 3
movl -4(%rbp), %eax
addl $3, %eax
; }
popq %rbp
ret
สำหรับ "int a = 1;" ซีพียูเห็นคำสั่ง "เก็บค่า 1 ที่ที่อยู่ (ค่าของ register rbp, ลบ 4)" มันรู้ตำแหน่งที่จะเก็บค่า 1 เพราะโปรแกรมบอก
เช่นเดียวกันคำสั่งถัดไปบอกว่า "โหลดค่าที่อยู่ (ค่า register rbp ลบ 4) ลงใน register eax" คอมพิวเตอร์ไม่จำเป็นต้องรู้เกี่ยวกับสิ่งต่าง ๆ เช่นตัวแปร
%rsp
ตัวชี้สแต็กของ CPU %rbp
คือรีจิสเตอร์ที่อ้างถึงบิตของสแต็กที่ใช้โดยฟังก์ชันปัจจุบัน การใช้การลงทะเบียนสองครั้งช่วยให้การดีบักง่ายขึ้น
เมื่อคอมไพเลอร์หรือล่ามพบการประกาศของตัวแปรมันจะตัดสินใจว่าที่อยู่ใดที่มันจะใช้เพื่อเก็บตัวแปรนั้นแล้วบันทึกที่อยู่ในตารางสัญลักษณ์ เมื่อพบการอ้างอิงตัวแปรถัดไปที่อยู่จากตารางสัญลักษณ์จะถูกแทนที่
ที่อยู่ที่บันทึกไว้ในตารางสัญลักษณ์อาจเป็นออฟเซ็ตจากการลงทะเบียน (เช่นตัวชี้สแต็ก) แต่นั่นเป็นรายละเอียดการใช้งาน
วิธีการที่แน่นอนขึ้นอยู่กับสิ่งที่คุณกำลังพูดถึงและคุณต้องการไปลึกแค่ไหน ตัวอย่างเช่นการจัดเก็บไฟล์ในฮาร์ดไดรฟ์นั้นแตกต่างจากการจัดเก็บบางอย่างในหน่วยความจำหรือการจัดเก็บบางอย่างในฐานข้อมูล แม้ว่าแนวคิดจะคล้ายกัน และวิธีที่คุณทำในระดับการเขียนโปรแกรมเป็นคำอธิบายที่แตกต่างจากวิธีที่คอมพิวเตอร์ใช้ในระดับ I / O
ระบบส่วนใหญ่ใช้กลไกไดเรกทอรี / ดัชนี / รีจิสตรี้บางประเภทเพื่อให้คอมพิวเตอร์สามารถค้นหาและเข้าถึงข้อมูลได้ ดัชนี / ไดเรกทอรีนี้จะมีหนึ่งหรือมากกว่าหนึ่งคีย์และที่อยู่ที่ข้อมูลอยู่ในจริง (ไม่ว่าจะเป็นฮาร์ดไดรฟ์, RAM, ฐานข้อมูล ฯลฯ )
ตัวอย่างโปรแกรมคอมพิวเตอร์
โปรแกรมคอมพิวเตอร์สามารถเข้าถึงหน่วยความจำได้หลายวิธี โดยทั่วไปแล้วระบบปฏิบัติการจะให้พื้นที่ที่อยู่ของโปรแกรมและโปรแกรมสามารถทำสิ่งที่ต้องการด้วยพื้นที่ที่อยู่นั้น มันสามารถเขียนโดยตรงไปยังที่อยู่ใด ๆ ภายในพื้นที่หน่วยความจำและสามารถติดตามได้ว่ามันต้องการ บางครั้งสิ่งนี้จะแตกต่างกันไปตามภาษาการเขียนโปรแกรมและระบบปฏิบัติการหรือแม้กระทั่งตามเทคนิคที่โปรแกรมเมอร์ต้องการ
ดังที่กล่าวไว้ในคำตอบอื่น ๆ การเข้ารหัสหรือการเขียนโปรแกรมที่ใช้แตกต่างกันไป แต่โดยทั่วไปแล้วเบื้องหลังมันใช้อะไรบางอย่างเช่นกองซ้อน มีรีจิสเตอร์ที่เก็บตำแหน่งหน่วยความจำที่สแต็กปัจจุบันเริ่มต้นจากนั้นวิธีการรู้ว่าสแต็กหรือฟังก์ชันนั้นอยู่ที่ไหน
ในภาษาการเขียนโปรแกรมระดับสูงกว่านั้นจะดูแลทุกอย่างให้คุณ สิ่งที่คุณต้องทำคือการประกาศตัวแปรและเก็บบางสิ่งไว้ในตัวแปรนั้นและมันจะสร้างสแต็คและอาร์เรย์ที่จำเป็นเบื้องหลังสำหรับคุณ
แต่เมื่อพิจารณาว่าการเขียนโปรแกรมที่หลากหลายนั้นไม่มีคำตอบเดียวเนื่องจากโปรแกรมเมอร์สามารถเลือกที่จะเขียนไปยังที่อยู่ใด ๆ ภายในพื้นที่ที่จัดสรรได้ตลอดเวลา (สมมติว่าเขาใช้ภาษาการเขียนโปรแกรมที่อนุญาต) จากนั้นเขาสามารถเก็บตำแหน่งของมันไว้ในอาร์เรย์หรือแม้แต่รหัสที่ยากในโปรแกรม (เช่นตัวแปร "อัลฟา" จะถูกเก็บไว้ที่จุดเริ่มต้นของสแต็กเสมอหรือเก็บไว้ใน 32 บิตแรกของหน่วยความจำที่จัดสรร)
สรุป
ดังนั้นโดยทั่วไปจะต้องมีกลไกบางอย่างอยู่เบื้องหลังซึ่งบอกคอมพิวเตอร์ว่ามีการจัดเก็บข้อมูลไว้ที่ใด หนึ่งในวิธีที่ได้รับความนิยมมากที่สุดคือการเรียงลำดับของดัชนี / ไดเร็กทอรีที่มีคีย์และที่อยู่หน่วยความจำ สิ่งนี้ถูกนำไปใช้ในรูปแบบต่างๆและมักจะถูกห่อหุ้มจากผู้ใช้ (และบางครั้งก็ถูกห่อหุ้มจากโปรแกรมเมอร์)
การอ้างอิง: คอมพิวเตอร์จะจดจำตำแหน่งที่เก็บสิ่งต่าง ๆ ได้อย่างไร
มันรู้เพราะแม่แบบและรูปแบบ
โปรแกรม / ฟังก์ชั่น / คอมพิวเตอร์ไม่รู้จริง ๆ ว่ามีอะไรบ้าง มันแค่คาดหวังว่าบางสิ่งจะอยู่ในที่ที่แน่นอน ลองใช้ตัวอย่าง
class simpleClass{
public:
int varA=58;
int varB=73;
simpleClass* nextObject=NULL;
};
คลาสใหม่ 'simpleClass' มี 3 ตัวแปรที่สำคัญ - จำนวนเต็มสองตัวที่สามารถมีข้อมูลบางอย่างเมื่อเราต้องการและชี้ไปยัง 'simpleClass object' สมมติว่าเราอยู่ในเครื่อง 32- บิตเพื่อความเรียบง่าย 'gcc' หรือคอมไพเลอร์ 'C' อื่นจะสร้างแม่แบบให้เราทำงานด้วยเพื่อจัดสรรข้อมูลบางส่วน
ประเภทที่เรียบง่าย
ประการแรกเมื่อมีการใช้คำสำคัญสำหรับประเภทที่เรียบง่ายเช่น 'int' จะมีการบันทึกย่อโดยคอมไพเลอร์ในไฟล์ '.data' หรือ '.bss' ของไฟล์ที่ปฏิบัติการได้เพื่อให้เมื่อถูกเรียกใช้งานโดยระบบปฏิบัติการ สามารถใช้ได้กับโปรแกรม คำหลัก 'int' จะจัดสรร 4 ไบต์ (32 บิต) ในขณะที่ 'long int' จะจัดสรร 8 ไบต์ (64 บิต)
บางครั้งในลักษณะเซลล์ต่อเซลล์ตัวแปรอาจมาทันทีหลังจากคำแนะนำที่ควรจะโหลดลงในหน่วยความจำดังนั้นมันจะมีลักษณะเช่นนี้ในการประกอบหลอก:
...
clear register EAX
clear register EBX
load the immediate (next) value into EAX
5
copy the value in register EAX to register EBX
...
สิ่งนี้จะลงท้ายด้วยค่า '5' ใน EAX เช่นเดียวกับ EBX
ในขณะที่โปรแกรมดำเนินการคำสั่งทุกคำสั่งจะดำเนินการยกเว้น '5' ตั้งแต่โหลดทันทีอ้างอิงและทำให้ CPU ข้ามมัน
ข้อเสียของวิธีนี้คือมันใช้ได้จริงสำหรับค่าคงที่เท่านั้นเนื่องจากจะไม่สามารถเก็บอาร์เรย์ / บัฟเฟอร์ / สตริงไว้ในโค้ดของคุณได้ ดังนั้นโดยทั่วไปตัวแปรส่วนใหญ่จะถูกเก็บไว้ในส่วนหัวของโปรแกรม
หากจำเป็นต้องเข้าถึงหนึ่งในตัวแปรแบบไดนามิกเหล่านี้หนึ่งสามารถปฏิบัติกับค่าทันทีราวกับว่ามันเป็นตัวชี้:
...
clear register EAX
clear register EBX
load the immediate value into EAX
0x0AF2CE66 (Let's say this is the address of a cell containing '5')
load the value pointed to by EAX into EBX
...
สิ่งนี้จะลงท้ายด้วยค่า '0x0AF2CE66' ใน register EAX และค่า '5' ใน register EBX เราสามารถเพิ่มค่าในการลงทะเบียนด้วยกันได้ดังนั้นเราจะสามารถหาองค์ประกอบของอาร์เรย์หรือสตริงโดยใช้วิธีนี้
อีกจุดที่สำคัญคือคนที่สามารถเก็บค่าเมื่อใช้ที่อยู่ในลักษณะที่คล้ายกันเพื่อให้สามารถอ้างอิงค่าที่เซลล์เหล่านั้นในภายหลัง
ประเภทที่ซับซ้อน
ถ้าเราสร้างสองวัตถุของคลาสนี้:
simpleClass newObjA;
simpleClass newObjB;
จากนั้นเราสามารถกำหนดตัวชี้ไปยังวัตถุที่สองให้กับเขตข้อมูลที่มีอยู่ในวัตถุแรก:
newObjA.nextObject=&newObjB;
ขณะนี้โปรแกรมสามารถคาดหวังว่าจะค้นหาที่อยู่ของวัตถุที่สองภายในฟิลด์ตัวชี้ของวัตถุแรก ในความทรงจำนี่จะมีลักษณะดังนี้:
newObjA: 58
73
&newObjB
...
newObjB: 58
73
NULL
ข้อเท็จจริงสำคัญอย่างหนึ่งที่ควรทราบที่นี่คือ 'newObjA' และ 'newObjB' ไม่มีชื่อเมื่อมีการรวบรวม พวกเขาเป็นเพียงสถานที่ที่เราคาดหวังว่าข้อมูลบางอย่างจะอยู่ที่ ดังนั้นหากเราเพิ่ม 2 เซลล์ใน & newObjA เราจะพบเซลล์ที่ทำหน้าที่เป็น 'nextObject' ดังนั้นหากเราทราบที่อยู่ของ 'newObjA' และที่ที่เซลล์ 'nextObject' สัมพันธ์กับที่อยู่นั้นเราสามารถทราบที่อยู่ของ 'newObjB' ได้:
...
load the immediate value into EAX
&newObjA
add the immediate value to EAX
2
load the value in EAX into EBX
สิ่งนี้จะลงท้ายด้วย '2 + & newObjA' ใน 'EAX' และ '& newObjB' ใน 'EBX'
แม่แบบ / รูปแบบ
เมื่อคอมไพเลอร์รวบรวมคำจำกัดความของคลาสมันเป็นการรวบรวมวิธีการจัดรูปแบบวิธีการเขียนรูปแบบและวิธีการอ่านจากรูปแบบ
ตัวอย่างที่ให้ไว้ข้างต้นเป็นแม่แบบสำหรับรายการที่เชื่อมโยงโดยลำพังที่มีตัวแปร 'int' สองรายการ สิ่งปลูกสร้างประเภทนี้มีความสำคัญมากสำหรับการจัดสรรหน่วยความจำแบบไดนามิกพร้อมกับต้นไม้ไบนารีและต้นไม้ การใช้งานจริงของต้นไม้ n-ary จะเป็นระบบไฟล์ที่ประกอบด้วยไดเรกทอรีที่ชี้ไปยังไฟล์ไดเรกทอรีหรืออินสแตนซ์อื่น ๆ ที่ได้รับการยอมรับจากไดรเวอร์ / ระบบปฏิบัติการ
ในการเข้าถึงองค์ประกอบทั้งหมดให้คิดว่ามีหนอนตัวหนึ่งทำงานอยู่ในโครงสร้างขึ้นและลง ด้วยวิธีนี้โปรแกรม / ฟังก์ชั่น / คอมพิวเตอร์ไม่รู้อะไรเลยมันแค่ประมวลคำสั่งเพื่อย้ายข้อมูลไปรอบ ๆ