คอมพิวเตอร์จำได้อย่างไรว่าพวกเขาเก็บของไว้ที่ไหน


32

เมื่อคอมพิวเตอร์จัดเก็บตัวแปรเมื่อโปรแกรมต้องการรับค่าของตัวแปรคอมพิวเตอร์จะทราบได้อย่างไรว่าจะค้นหาหน่วยความจำสำหรับค่าของตัวแปรนั้นได้อย่างไร


17
มันไม่ได้; "คอมพิวเตอร์" นั้นลืมไปอย่างสมบูรณ์ เราต้องฮาร์ดโค้ดที่อยู่ทั้งหมด (ซึ่งลดความซับซ้อนลงเล็กน้อย แต่ไม่มากเกินไป)
Raphael

1
@ ราฟาเอล: มาพูดคุยกันเรื่อง "เราต้องฮาร์ดโค้ดที่อยู่ฐาน"
phresnel

ทุกครั้งที่คุณประกาศตัวแปรโปรแกรมที่รับผิดชอบในการเรียกใช้รหัสของคุณจะมีชื่อตัวแปรพร้อมที่อยู่ของมันใน hashtable (aka namespace) ฉันขอแนะนำให้อ่านหนังสือ "โครงสร้างและการใช้งานโปรแกรมคอมพิวเตอร์ (SICP) เพื่อทำความคุ้นเคยกับรายละเอียดเล็ก ๆ น้อย ๆ เหล่านี้
Abhirath Mahipal

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

1
@AbhirathMahipal: ตัวแปรไม่จำเป็นต้องมีที่อยู่ในเวลารวบรวมหรือแม้กระทั่งเวลาทำงาน “ namespace” เป็นแนวคิดภาษาในขณะที่ตาราง (แฮชหรืออื่น ๆ ) เป็นรายละเอียดการใช้งาน ชื่อต้องการพยักหน้าคงอยู่ในโปรแกรมเมื่อมีการเรียกใช้
PJTraill

คำตอบ:


31

ฉันขอแนะนำให้คุณดูในโลกมหัศจรรย์ของการก่อสร้างคอมไพเลอร์! คำตอบคือมันเป็นกระบวนการที่ค่อนข้างซับซ้อน

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

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

มีเวทมนตร์เล็กน้อยที่เรียกว่าตัวชี้สแต็กซึ่งเป็นรีจิสเตอร์ที่เก็บที่อยู่ของสแต็กปัจจุบันที่เริ่มต้นเสมอ

ตัวแปรแต่ละตัวจะได้รับ "ออฟเซ็ตสแต็ก" ซึ่งเป็นตำแหน่งที่เก็บสแต็ก จากนั้นเมื่อโปรแกรมต้องการเข้าถึงตัวแปรxคอมไพเลอร์จะแทนที่xด้วยSTACK_POINTER + x_offsetเพื่อให้ได้สถานที่จริงที่เก็บไว้ในหน่วยความจำ

โปรดทราบว่านี่คือสาเหตุที่คุณได้รับพอยน์เตอร์กลับมาเมื่อคุณใช้mallocหรือnewใน C หรือ C ++ คุณไม่สามารถระบุได้ว่าค่าที่จัดสรรฮีปอยู่ในหน่วยความจำอย่างแน่นอนดังนั้นคุณต้องเก็บตัวชี้ไว้ ตัวชี้นั้นจะอยู่บนสแต็ก แต่จะชี้ไปที่ฮีป

รายละเอียดของการอัปเดตสแต็คสำหรับการเรียกใช้ฟังก์ชันและการส่งคืนนั้นซับซ้อนดังนั้นฉันขอแนะนำThe Dragon BookหรือThe Tiger Bookหากคุณสนใจ


24

เมื่อคอมพิวเตอร์จัดเก็บตัวแปรเมื่อโปรแกรมต้องการรับค่าของตัวแปรคอมพิวเตอร์จะทราบได้อย่างไรว่าจะค้นหาหน่วยความจำสำหรับค่าของตัวแปรนั้นได้อย่างไร

โปรแกรมบอกมัน คอมพิวเตอร์ไม่มีแนวคิดเรื่อง "ตัวแปร" ในตัว - เป็นภาษาระดับสูงอย่างสิ้นเชิง!

นี่คือโปรแกรม 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" คอมพิวเตอร์ไม่จำเป็นต้องรู้เกี่ยวกับสิ่งต่าง ๆ เช่นตัวแปร


2
เพื่อเชื่อมต่อสิ่งนี้กับคำตอบของ jmite เป็น%rspตัวชี้สแต็กของ CPU %rbpคือรีจิสเตอร์ที่อ้างถึงบิตของสแต็กที่ใช้โดยฟังก์ชันปัจจุบัน การใช้การลงทะเบียนสองครั้งช่วยให้การดีบักง่ายขึ้น
MSalters

2

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

ที่อยู่ที่บันทึกไว้ในตารางสัญลักษณ์อาจเป็นออฟเซ็ตจากการลงทะเบียน (เช่นตัวชี้สแต็ก) แต่นั่นเป็นรายละเอียดการใช้งาน


0

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

ระบบส่วนใหญ่ใช้กลไกไดเรกทอรี / ดัชนี / รีจิสตรี้บางประเภทเพื่อให้คอมพิวเตอร์สามารถค้นหาและเข้าถึงข้อมูลได้ ดัชนี / ไดเรกทอรีนี้จะมีหนึ่งหรือมากกว่าหนึ่งคีย์และที่อยู่ที่ข้อมูลอยู่ในจริง (ไม่ว่าจะเป็นฮาร์ดไดรฟ์, RAM, ฐานข้อมูล ฯลฯ )

ตัวอย่างโปรแกรมคอมพิวเตอร์

โปรแกรมคอมพิวเตอร์สามารถเข้าถึงหน่วยความจำได้หลายวิธี โดยทั่วไปแล้วระบบปฏิบัติการจะให้พื้นที่ที่อยู่ของโปรแกรมและโปรแกรมสามารถทำสิ่งที่ต้องการด้วยพื้นที่ที่อยู่นั้น มันสามารถเขียนโดยตรงไปยังที่อยู่ใด ๆ ภายในพื้นที่หน่วยความจำและสามารถติดตามได้ว่ามันต้องการ บางครั้งสิ่งนี้จะแตกต่างกันไปตามภาษาการเขียนโปรแกรมและระบบปฏิบัติการหรือแม้กระทั่งตามเทคนิคที่โปรแกรมเมอร์ต้องการ

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

ในภาษาการเขียนโปรแกรมระดับสูงกว่านั้นจะดูแลทุกอย่างให้คุณ สิ่งที่คุณต้องทำคือการประกาศตัวแปรและเก็บบางสิ่งไว้ในตัวแปรนั้นและมันจะสร้างสแต็คและอาร์เรย์ที่จำเป็นเบื้องหลังสำหรับคุณ

แต่เมื่อพิจารณาว่าการเขียนโปรแกรมที่หลากหลายนั้นไม่มีคำตอบเดียวเนื่องจากโปรแกรมเมอร์สามารถเลือกที่จะเขียนไปยังที่อยู่ใด ๆ ภายในพื้นที่ที่จัดสรรได้ตลอดเวลา (สมมติว่าเขาใช้ภาษาการเขียนโปรแกรมที่อนุญาต) จากนั้นเขาสามารถเก็บตำแหน่งของมันไว้ในอาร์เรย์หรือแม้แต่รหัสที่ยากในโปรแกรม (เช่นตัวแปร "อัลฟา" จะถูกเก็บไว้ที่จุดเริ่มต้นของสแต็กเสมอหรือเก็บไว้ใน 32 บิตแรกของหน่วยความจำที่จัดสรร)

สรุป

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

การอ้างอิง: คอมพิวเตอร์จะจดจำตำแหน่งที่เก็บสิ่งต่าง ๆ ได้อย่างไร


0

มันรู้เพราะแม่แบบและรูปแบบ

โปรแกรม / ฟังก์ชั่น / คอมพิวเตอร์ไม่รู้จริง ๆ ว่ามีอะไรบ้าง มันแค่คาดหวังว่าบางสิ่งจะอยู่ในที่ที่แน่นอน ลองใช้ตัวอย่าง

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 จะเป็นระบบไฟล์ที่ประกอบด้วยไดเรกทอรีที่ชี้ไปยังไฟล์ไดเรกทอรีหรืออินสแตนซ์อื่น ๆ ที่ได้รับการยอมรับจากไดรเวอร์ / ระบบปฏิบัติการ

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


คำว่า 'เทมเพลต' และ 'รูปแบบ' ที่ใช้ในที่นี้ไม่ปรากฏในหนังสือรวบรวมหรือตำราแปลใด ๆ ที่ฉันเคยเห็นและไม่มีเหตุผลใดที่จะใช้ทั้งสองคำสำหรับสิ่งที่ไม่มีอยู่เดิม ตัวแปรมีที่อยู่และ / หรือออฟเซ็ตนั่นคือทั้งหมดที่คุณต้องรู้
user207421

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