Ketika komputer menyimpan variabel, ketika suatu program perlu mendapatkan nilai variabel, bagaimana komputer tahu di mana harus mencari dalam memori untuk nilai variabel itu?
compilers
memory-access
variable-binding
MCMastery
sumber
sumber
Jawaban:
Saya sarankan Anda melihat dunia Konstruksi Kompiler yang indah! Jawabannya adalah ini sedikit proses yang rumit.
Untuk mencoba memberi Anda intuisi, ingatlah bahwa nama variabel murni ada untuk kepentingan programmer. Komputer pada akhirnya akan mengubah segalanya menjadi alamat di bagian akhir.
Variabel lokal (umumnya) disimpan di stack: yaitu, mereka adalah bagian dari struktur data yang mewakili pemanggilan fungsi. Kita dapat menentukan daftar lengkap variabel yang akan digunakan suatu fungsi (mungkin) dengan melihat fungsi itu, sehingga kompiler dapat melihat berapa banyak variabel yang dibutuhkan untuk fungsi ini dan berapa banyak ruang yang dibutuhkan setiap variabel.
Ada sedikit keajaiban yang disebut stack pointer, yang merupakan register yang selalu menyimpan alamat di mana tumpukan saat ini dimulai.
Setiap variabel diberi "tumpukan offset", yang merupakan tempat dalam tumpukan itu disimpan. Kemudian, ketika program perlu mengakses variabel
x
, kompilator menggantikannyax
denganSTACK_POINTER + x_offset
, untuk mendapatkan tempat fisik sebenarnya yang disimpan dalam memori.Perhatikan bahwa, inilah sebabnya Anda mendapatkan pointer kembali ketika Anda menggunakan
malloc
ataunew
dalam C atau C ++. Anda tidak dapat menentukan di mana tepatnya dalam memori nilai yang dialokasikan heap, jadi Anda harus menyimpan pointer ke sana. Pointer itu ada di stack, tetapi akan menunjuk ke heap.Rincian memperbarui tumpukan untuk pemanggilan fungsi dan pengembalian adalah rumit, jadi saya akan merekomendasikan The Dragon Book atau The Tiger Book jika Anda tertarik.
sumber
Program mengatakannya. Komputer tidak secara alami memiliki konsep "variabel" - itu sepenuhnya hal bahasa tingkat tinggi!
Inilah program C:
dan inilah kode perakitan yang dikompilasinya: (komentar dimulai dengan
;
)Untuk "int a = 1;" CPU melihat instruksi "menyimpan nilai 1 di alamat (nilai register rbp, minus 4)". Ia tahu di mana menyimpan nilai 1 karena program mengatakannya.
Demikian juga, instruksi selanjutnya mengatakan "memuat nilai di alamat (nilai register rbp, minus 4) ke register eax". Komputer tidak perlu tahu tentang hal-hal seperti variabel.
sumber
%rsp
adalah penunjuk tumpukan CPU.%rbp
adalah register yang mengacu pada sedikit tumpukan yang digunakan oleh fungsi saat ini. Menggunakan dua register menyederhanakan debugging.Ketika kompiler atau interpreter menemukan deklarasi variabel, ia memutuskan alamat apa yang akan digunakan untuk menyimpan variabel itu, dan kemudian mencatat alamat dalam tabel simbol. Ketika referensi selanjutnya untuk variabel itu ditemukan, alamat dari tabel simbol diganti.
Alamat yang direkam dalam tabel simbol mungkin merupakan offset dari register (seperti stack pointer) tapi itu detail implementasi.
sumber
Metode yang tepat tergantung pada apa yang Anda bicarakan dan seberapa dalam Anda ingin melangkah. Misalnya, menyimpan file pada hard drive berbeda dari menyimpan sesuatu di memori atau menyimpan sesuatu di database. Meski konsepnya mirip. Dan bagaimana Anda melakukannya pada level pemrograman adalah penjelasan yang berbeda dari bagaimana komputer melakukannya pada level I / O.
Sebagian besar sistem menggunakan semacam mekanisme direktori / indeks / registri untuk memungkinkan komputer menemukan dan mengakses data. Indeks / direktori ini akan berisi satu atau lebih kunci, dan alamat data sebenarnya terletak di (apakah itu hard drive, RAM, database, dll.).
Contoh Program Komputer
Program komputer dapat mengakses memori dengan berbagai cara. Biasanya sistem operasi memberi ruang alamat pada program, dan program dapat melakukan apa yang diinginkannya dengan ruang alamat tersebut. Ia dapat menulis secara langsung ke alamat apa pun di dalam ruang memorinya, dan ia dapat melacaknya seperti yang diinginkannya. Ini kadang-kadang akan berbeda menurut bahasa pemrograman dan sistem operasi, atau bahkan menurut teknik yang disukai programmer.
Seperti disebutkan dalam beberapa jawaban lain, pengkodean atau pemrograman yang digunakan berbeda, tetapi biasanya di balik layar ia menggunakan sesuatu seperti tumpukan. Ini memiliki register yang menyimpan lokasi memori di mana tumpukan saat ini dimulai, dan kemudian metode untuk mengetahui di mana di tumpukan itu fungsi atau variabel.
Dalam banyak bahasa pemrograman tingkat yang lebih tinggi, ini akan menangani semua itu untuk Anda. Yang harus Anda lakukan adalah mendeklarasikan variabel, dan menyimpan sesuatu di variabel itu, dan itu menciptakan tumpukan dan array yang diperlukan di belakang layar untuk Anda.
Tetapi mengingat betapa serbagunanya pemrograman, sebenarnya tidak ada satu jawaban, karena seorang programmer dapat memilih untuk menulis secara langsung ke alamat mana saja di dalam ruang yang dialokasikan kapan saja (dengan asumsi ia menggunakan bahasa pemrograman yang memungkinkan hal itu). Kemudian ia dapat menyimpan lokasinya dalam sebuah array, atau bahkan hanya kode keras ke dalam program (yaitu variabel "alpha" selalu disimpan di awal tumpukan atau selalu disimpan dalam 32 bit pertama memori yang dialokasikan).
Ringkasan
Jadi pada dasarnya, harus ada beberapa mekanisme di belakang layar yang memberitahu komputer di mana data disimpan. Salah satu cara yang paling populer adalah semacam indeks / direktori yang berisi kunci dan alamat memori. Ini diimplementasikan dalam segala macam cara dan biasanya dienkapsulasi dari pengguna (dan kadang-kadang bahkan dienkapsulasi dari programmer).
Referensi: Bagaimana komputer mengingat di mana mereka menyimpan barang?
sumber
Itu tahu karena template dan format.
Program / fungsi / komputer tidak benar-benar tahu di mana ada sesuatu. Itu hanya mengharapkan sesuatu berada di tempat tertentu. Mari kita gunakan sebuah contoh.
Kelas baru kami 'simpleClass' berisi 3 variabel penting - dua integer yang dapat berisi beberapa data saat kami membutuhkannya, dan sebuah penunjuk ke 'objek simpleClass' lainnya. Mari kita asumsikan bahwa kita menggunakan mesin 32-bit demi kesederhanaan. 'gcc' atau kompiler 'C' yang lain akan membuat templat yang dapat kita gunakan untuk mengalokasikan beberapa data.
Tipe Sederhana
Pertama, ketika seseorang menggunakan kata kunci untuk tipe sederhana seperti 'int', sebuah catatan dibuat oleh kompiler di bagian file yang dapat dieksekusi yaitu '.data' atau '.bss' sehingga ketika dijalankan oleh sistem operasi, datanya adalah tersedia untuk program ini. Kata kunci 'int' akan mengalokasikan 4 byte (32 bit), sedangkan 'int panjang' akan mengalokasikan 8 byte (64 bit).
Kadang-kadang, dengan cara sel demi sel, suatu variabel dapat muncul tepat setelah instruksi yang seharusnya memuatnya ke dalam memori, jadi itu akan terlihat seperti ini dalam pseudo-assembly:
Ini akan berakhir dengan nilai '5' di EAX dan juga EBX.
Ketika program dijalankan, setiap instruksi dijalankan kecuali untuk '5' karena memuat langsung referensi itu dan membuat CPU melewatinya.
Kelemahan dari metode ini adalah hanya benar-benar praktis untuk konstanta, karena tidak praktis untuk menyimpan array / buffer / string di tengah-tengah kode Anda. Jadi, secara umum, sebagian besar variabel disimpan di header program.
Jika seseorang perlu mengakses salah satu variabel dinamis ini, maka seseorang dapat memperlakukan nilai langsung seolah-olah itu adalah pointer:
Ini akan diakhiri dengan nilai '0x0AF2CE66' di register EAX dan nilai '5' di register EBX. Kita juga bisa menambahkan nilai dalam register bersama, jadi kita bisa menemukan elemen array atau string menggunakan metode ini.
Poin penting lainnya adalah bahwa seseorang dapat menyimpan nilai saat menggunakan alamat dengan cara yang sama, sehingga orang dapat merujuk nilai pada sel tersebut nanti.
Jenis yang kompleks
Jika kita membuat dua objek dari kelas ini:
lalu kita bisa menetapkan pointer ke objek kedua ke bidang yang tersedia untuknya di objek pertama:
Sekarang program dapat berharap untuk menemukan alamat objek kedua dalam bidang pointer objek pertama. Dalam memori, ini akan terlihat seperti:
Satu fakta yang sangat penting untuk dicatat di sini adalah bahwa 'newObjA' dan 'newObjB' tidak memiliki nama saat dikompilasi. Itu hanya tempat di mana kami mengharapkan beberapa data berada. Jadi, jika kita menambahkan 2 sel ke & newObjA maka kita menemukan sel yang bertindak sebagai 'nextObject'. Oleh karena itu, jika kita mengetahui alamat 'newObjA' dan di mana sel 'nextObject' relatif terhadapnya, maka kita dapat mengetahui alamat 'newObjB':
Ini akan berakhir dengan '2 + & newObjA' di 'EAX' dan '& newObjB' di 'EBX'.
Templat / Format
Ketika kompiler mengkompilasi definisi kelas, itu benar-benar mengkompilasi cara untuk membuat format, cara menulis ke format, dan cara membaca dari format.
Contoh yang diberikan di atas adalah templat untuk daftar yang terhubung sendiri dengan dua variabel 'int'. Jenis konstruksi ini sangat penting untuk alokasi memori dinamis, bersama dengan pohon biner dan n-ary. Aplikasi praktis dari n-ary tree adalah sistem file yang terdiri dari direktori yang menunjuk ke file, direktori, atau instance lain yang dikenali oleh driver / sistem operasi.
Untuk mengakses semua elemen, pikirkan tentang inchworm yang bekerja dengan cara naik turun struktur. Dengan cara ini, program / fungsi / komputer tidak tahu apa-apa, itu hanya menjalankan instruksi untuk memindahkan data.
sumber