Apa yang dimaksud dengan "Memori yang dialokasikan pada waktu kompilasi"?

159

Dalam bahasa pemrograman seperti C dan C ++, orang sering merujuk pada alokasi memori statis dan dinamis. Saya mengerti konsepnya tetapi frasa "Semua memori dialokasikan (dicadangkan) selama waktu kompilasi" selalu membingungkan saya.

Kompilasi, seperti yang saya mengerti, mengkonversi kode C / C ++ tingkat tinggi ke bahasa mesin dan menghasilkan file yang dapat dieksekusi. Bagaimana memori "dialokasikan" dalam file yang dikompilasi? Bukankah memori selalu dialokasikan dalam RAM dengan semua hal manajemen memori virtual?

Bukankah alokasi memori menurut definisi konsep runtime?

Jika saya membuat variabel statis 1KB yang dialokasikan dalam kode C / C ++ saya, apakah itu akan meningkatkan ukuran executable dengan jumlah yang sama?

Ini adalah salah satu halaman di mana frasa tersebut digunakan di bawah judul "Alokasi statis".

Kembali ke Dasar: Alokasi memori, jalan-jalan di sepanjang sejarah

Talha Sayed
sumber
kode dan data benar-benar terpisah di sebagian besar arsitektur modern. sementara file sumber berisi kedua kode data di tempat yang sama, nampan hanya memiliki referensi ke data. Ini berarti data statis dalam sumber hanya diselesaikan sebagai referensi.
Cholthi Paul Ttiopic

Jawaban:

184

Memori yang dialokasikan pada waktu kompilasi berarti kompiler menyelesaikan pada waktu kompilasi di mana hal-hal tertentu akan dialokasikan di dalam peta memori proses.

Misalnya, pertimbangkan array global:

int array[100];

Compiler mengetahui pada waktu kompilasi ukuran array dan ukuran suatu int, sehingga ia mengetahui seluruh ukuran array pada waktu kompilasi. Juga variabel global memiliki durasi penyimpanan statis secara default: itu dialokasikan di area memori statis dari ruang memori proses (.data / .bss bagian). Mengingat informasi itu, kompilator memutuskan selama kompilasi di alamat mana dari area memori statis array .

Tentu saja alamat memori itu adalah alamat virtual. Program ini mengasumsikan bahwa ia memiliki seluruh ruang memori sendiri (Dari 0x00000000 hingga 0xFFFFFFFF misalnya). Itu sebabnya kompiler dapat melakukan asumsi seperti "Oke, array akan ada di alamat 0x00A33211". Pada saat runtime, alamat diterjemahkan ke alamat real / hardware oleh MMU dan OS.

Nilai yang diinisialisasi hal-hal penyimpanan statis sedikit berbeda. Sebagai contoh:

int array[] = { 1 , 2 , 3 , 4 };

Dalam contoh pertama kami, kompiler hanya memutuskan di mana array akan dialokasikan, menyimpan informasi itu di executable.
Dalam hal hal-hal yang diinisialisasi nilai, kompiler juga menyuntikkan nilai awal array ke dalam executable, dan menambahkan kode yang memberitahu program loader bahwa setelah alokasi array pada saat program dimulai, array harus diisi dengan nilai-nilai ini.

Berikut adalah dua contoh rakitan yang dihasilkan oleh kompiler (GCC4.8.1 dengan target x86):

Kode C ++:

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

Perakitan output:

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

Seperti yang Anda lihat, nilai langsung disuntikkan ke majelis. Dalam array a, kompiler menghasilkan inisialisasi nol 16 byte, karena Standar mengatakan bahwa hal-hal yang tersimpan statis harus diinisialisasi ke nol secara default:

8.5.9 (Inisialisasi) [Catatan]:
Setiap objek durasi penyimpanan statis nol diinisialisasi pada startup program sebelum inisialisasi lainnya terjadi. Dalam beberapa kasus, inisialisasi tambahan dilakukan kemudian.

Saya selalu menyarankan orang untuk membongkar kode mereka untuk melihat apa yang sebenarnya dilakukan oleh kompiler dengan kode C ++. Ini berlaku dari kelas / durasi penyimpanan (seperti pertanyaan ini) hingga optimisasi kompiler tingkat lanjut. Anda bisa menginstruksikan kompiler Anda untuk membuat perakitan, tetapi ada alat yang bagus untuk melakukan ini di Internet dengan ramah. Favorit saya adalah GCC Explorer .

Manu343726
sumber
2
Terima kasih. Ini banyak menjelaskan. Jadi kompiler mengeluarkan sesuatu yang setara dengan "cadangan memori dari 0xABC hingga 0xXYZ untuk array variabel [] dll." dan kemudian loader menggunakannya untuk mengalokasikannya tepat sebelum menjalankan program?
Talha Sayed
1
@ Talha Tepat mengatakan. Lihat hasil edit untuk melihat contohnya
Manu343726
2
@Secko Saya telah menyederhanakan hal-hal. Ini hanya menyebutkan tentang program ini bekerja melalui memori virtual, tetapi karena pertanyaannya bukan tentang memori virtual saya belum memperluas topik. Saya hanya menunjuk bahwa kompiler dapat melakukan asumsi tentang alamat memori pada waktu kompilasi, berkat memori virtual.
Manu343726
2
@Secko ya. mmm "dihasilkan" adalah istilah yang lebih baik saya pikir.
Manu343726
2
"Ini dialokasikan di area mamory statis ruang memori proses" Membaca yang mengalokasikan beberapa area susu statis di ruang memori proses saya.
Radiodef
27

Memori yang dialokasikan pada waktu kompilasi berarti tidak akan ada alokasi lebih lanjut pada waktu berjalan - tidak ada panggilan ke malloc, baru, atau metode alokasi dinamis lainnya. Anda akan memiliki jumlah penggunaan memori yang tetap walaupun Anda tidak membutuhkan semua memori itu sepanjang waktu.

Bukankah alokasi memori menurut definisi konsep runtime?

Memori tidak digunakan sebelum waktu berjalan, tetapi segera sebelum eksekusi mulai alokasi ditangani oleh sistem.

Jika saya membuat variabel statis 1KB yang dialokasikan dalam kode C / C ++ saya, apakah itu akan meningkatkan ukuran executable dengan jumlah yang sama?

Cukup mendeklarasikan statis tidak akan meningkatkan ukuran executable Anda lebih dari beberapa byte. Mendeklarasikannya dengan nilai awal yang bukan nol akan (untuk menyimpan nilai awal itu). Sebaliknya, penghubung hanya menambahkan jumlah 1KB ini ke persyaratan memori yang dibuat oleh loader sistem untuk Anda segera sebelum eksekusi.

mah
sumber
1
jika saya menulis static int i[4] = {2 , 3 , 5 ,5 }apakah ini akan meningkat dengan ukuran yang dapat dieksekusi sebesar 16 byte. Anda berkata "Cukup mendeklarasikan statis tidak akan meningkatkan ukuran executable Anda lebih dari beberapa byte. Mendeklarasikannya dengan nilai awal yang bukan nol akan" Mendeklarasikannya dengan nilai awal akan apa artinya.
Suraj Jain
Eksekusi Anda memiliki dua area untuk data statis - satu untuk statika yang tidak diinisialisasi dan satu untuk statika yang diinisialisasi. Area yang tidak diinisialisasi sebenarnya hanyalah indikasi ukuran; ketika program Anda dijalankan, ukuran itu digunakan untuk menumbuhkan area penyimpanan statis tetapi program itu sendiri tidak harus menyimpan apa pun lebih dari seberapa banyak data yang tidak diinisialisasi digunakan. Untuk statika yang diinisialisasi, program Anda harus memegang tidak hanya ukuran statis (masing-masing), tetapi juga apa yang diinisialisasi. Jadi dalam contoh Anda, program Anda akan memiliki 2, 3, 5, dan 5 di dalamnya.
mah
Implementasinya didefinisikan di mana ia ditempatkan / bagaimana dialokasikan, tapi saya tidak yakin saya mengerti kebutuhan untuk tahu.
mah
23

Memori yang dialokasikan dalam waktu kompilasi berarti bahwa ketika Anda memuat program, beberapa bagian dari memori akan segera dialokasikan dan ukuran dan (relatif) posisi alokasi ini ditentukan pada waktu kompilasi.

char a[32];
char b;
char c;

Ketiga variabel tersebut "dialokasikan pada waktu kompilasi", artinya kompiler menghitung ukurannya (yang diperbaiki) pada waktu kompilasi. Variabel aakan menjadi offset dalam memori, katakanlah, menunjuk ke alamat 0, bakan menunjuk ke alamat 33 dan cdi 34 (seandainya tidak ada optimasi penyelarasan). Jadi, mengalokasikan 1Kb data statis tidak akan meningkatkan ukuran kode Anda , karena itu hanya akan mengubah offset di dalamnya. Ruang aktual akan dialokasikan pada waktu pengambilan .

Alokasi memori nyata selalu terjadi dalam waktu yang berjalan, karena kernel perlu melacaknya dan memperbarui struktur data internal (berapa banyak memori yang dialokasikan untuk setiap proses, halaman, dan sebagainya). Perbedaannya adalah bahwa kompiler sudah mengetahui ukuran setiap data yang akan Anda gunakan dan ini dialokasikan segera setelah program Anda dijalankan.

Ingat juga bahwa kita berbicara tentang alamat relatif . Alamat asli tempat variabel akan ditempatkan akan berbeda. Pada waktu buka kernel akan mencadangkan beberapa memori untuk proses, katakanlah di alamat x, dan semua alamat yang dikodekan dalam file yang dapat dieksekusi akan ditambahkan oleh xbyte, sehingga variabel adalam contoh akan di alamat x, b di alamat x+33dan begitu seterusnya.

fede1024
sumber
17

Menambahkan variabel pada tumpukan yang mengambil N byte tidak (tentu) meningkatkan ukuran bin oleh N byte. Itu akan, pada kenyataannya, menambah tetapi beberapa byte sebagian besar waktu.
Mari kita mulai dengan contoh bagaimana menambahkan 1000 karakter ke kode Anda akan meningkatkan ukuran bin secara linear.

Jika 1k adalah string, dari seribu karakter, yang dinyatakan seperti itu

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

dan kemudian vim your_compiled_bin, Anda benar-benar dapat melihat string di tempat sampah di suatu tempat. Dalam hal itu, ya: executable akan menjadi 1 k lebih besar, karena berisi string secara penuh.
Namun, jika Anda mengalokasikan array ints, chars atau longs pada stack dan menetapkannya dalam satu lingkaran, sesuatu di sepanjang baris ini

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

kemudian, tidak: itu tidak akan menambah nampan ... dengan 1000*sizeof(int)
Alokasi pada waktu kompilasi berarti apa yang Anda pahami sekarang (berdasarkan komentar Anda): nampan yang dikompilasi berisi informasi yang dibutuhkan sistem untuk mengetahui berapa banyak memori fungsi / blok apa yang akan dibutuhkan ketika dieksekusi, bersama dengan informasi tentang ukuran tumpukan yang dibutuhkan aplikasi Anda. Itulah yang akan dialokasikan sistem ketika menjalankan nampan Anda, dan program Anda menjadi sebuah proses (well, pengeksekusian nampan Anda adalah proses yang ... well, Anda mengerti apa yang saya katakan).
Tentu saja, saya tidak akan melukis gambar lengkapnya di sini: Tempat sampah berisi informasi tentang seberapa besar tumpukan tempat sampah yang sebenarnya akan dibutuhkan. Berdasarkan informasi ini (antara lain), sistem akan mencadangkan sejumlah memori, yang disebut stack, bahwa program mendapat semacam pemerintahan bebas. Memori tumpukan masih dialokasikan oleh sistem, ketika proses (hasil dari bin Anda dijalankan) dimulai. Proses kemudian mengelola memori tumpukan untuk Anda. Ketika suatu fungsi atau loop (semua jenis blok) dipanggil / dieksekusi, variabel-variabel lokal ke blok tersebut didorong ke stack, dan mereka dihapus (memori stack "dibebaskan" begitu saja) untuk digunakan oleh yang lain fungsi / blok. Begitu mendeklarasikanint some_array[100]hanya akan menambahkan beberapa byte informasi tambahan ke nampan, yang memberitahu sistem bahwa fungsi X akan membutuhkan 100*sizeof(int)+ beberapa ruang pembukuan tambahan.

Elias Van Ootegem
sumber
Terima kasih banyak. Satu pertanyaan lagi, apakah variabel lokal untuk fungsi juga dialokasikan dengan cara yang sama selama waktu kompilasi?
Talha Sayed
@ TalhaSayed: Ya, itulah yang saya maksud ketika saya mengatakan: "informasi yang dibutuhkan sistem untuk mengetahui berapa banyak memori yang dibutuhkan oleh fungsi / blok." Saat Anda memanggil suatu fungsi, sistem akan mengalokasikan memori yang diperlukan untuk fungsi itu. Saat fungsi kembali, memori itu akan dibebaskan lagi.
Elias Van Ootegem
Adapun komentar dalam kode C Anda: Itu tidak benar-benar / selalu apa yang terjadi. Sebagai contoh, string kemungkinan besar akan dialokasikan hanya sekali, pada waktu kompilasi. Karena itu tidak pernah "dibebaskan" (juga saya pikir terminologi biasanya hanya digunakan ketika Anda mengalokasikan sesuatu secara dinamis), itidak "dibebaskan" atau keduanya. Jika iberada di memori, itu hanya akan didorong ke tumpukan, sesuatu yang tidak dibebaskan dalam arti kata, mengabaikan itu iatau cakan disimpan dalam register sepanjang waktu. Tentu saja, ini semua tergantung pada kompiler, yang berarti bukan hitam dan putih.
phant0m
@ phant0m: Saya tidak pernah mengatakan string tersebut dialokasikan pada stack, hanya pointernya saja, string itu sendiri akan berada dalam memori read-only. Saya tahu memori yang terkait dengan variabel lokal tidak dibebaskan dalam arti free()panggilan, tetapi memori tumpukan yang mereka gunakan gratis untuk digunakan oleh fungsi-fungsi lain begitu fungsi yang saya daftarkan kembali. Saya menghapus kode, karena mungkin membingungkan bagi beberapa orang
Elias Van Ootegem
Ah saya mengerti. Kalau begitu anggap komentar saya berarti "Saya bingung dengan kata-kata Anda."
phant0m
16

Pada banyak platform, semua alokasi global atau statis dalam setiap modul akan dikonsolidasikan oleh kompiler menjadi tiga atau lebih sedikit alokasi konsolidasi (satu untuk data yang tidak diinisialisasi (sering disebut "bss"), satu untuk data yang dapat diinisialisasi yang dapat ditulis (sering disebut "data" ), dan satu untuk data konstan ("const")), dan semua alokasi global atau statis dari setiap jenis dalam suatu program akan dikonsolidasikan oleh linker menjadi satu global untuk setiap jenis. Misalnya, dengan asumsi intempat byte, modul memiliki yang berikut ini sebagai satu-satunya alokasi statis:

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

itu akan memberitahu linker bahwa diperlukan 208 byte untuk bss, 16 byte untuk "data", dan 28 byte untuk "const". Selanjutnya, setiap referensi ke variabel akan diganti dengan pemilih area dan offset, sehingga a, b, c, d, dan e, akan diganti oleh bss + 0, const + 0, bss + 4, const + 24, data +0, atau bss + 204, masing-masing.

Ketika suatu program dihubungkan, semua area bss dari semua modul digabung menjadi satu; demikian juga data dan area const. Untuk setiap modul, alamat variabel bss-relatif akan ditingkatkan dengan ukuran semua area bss modul sebelumnya (sekali lagi, juga dengan data dan const). Jadi, ketika penghubung selesai, program apa pun akan memiliki satu alokasi bss, satu alokasi data, dan satu alokasi konstanta.

Ketika sebuah program dimuat, satu dari empat hal umumnya akan terjadi tergantung pada platform:

  1. Eksekusi akan menunjukkan berapa banyak byte yang dibutuhkan untuk setiap jenis data dan - untuk area data yang diinisialisasi, di mana konten awal dapat ditemukan. Ini juga akan mencakup daftar semua instruksi yang menggunakan alamat bss-, data-, atau konstanta. Sistem operasi atau loader akan mengalokasikan jumlah ruang yang sesuai untuk setiap area dan kemudian menambahkan alamat awal area tersebut ke setiap instruksi yang membutuhkannya.

  2. Sistem operasi akan mengalokasikan sepotong memori untuk menampung ketiga jenis data, dan memberi aplikasi pointer ke potongan memori itu. Kode apa pun yang menggunakan data statis atau global akan melakukan dereferensi relatif terhadap penunjuk itu (dalam banyak kasus, penunjuk akan disimpan dalam register untuk masa berlaku suatu aplikasi).

  3. Sistem operasi pada awalnya tidak akan mengalokasikan memori apa pun untuk aplikasi, kecuali untuk apa yang menyimpan kode binernya, tetapi hal pertama yang dilakukan aplikasi adalah meminta alokasi yang sesuai dari sistem operasi, yang selamanya disimpan dalam register.

  4. Sistem operasi pada awalnya tidak akan mengalokasikan ruang untuk aplikasi, tetapi aplikasi akan meminta alokasi yang sesuai pada saat startup (seperti di atas). Aplikasi akan menyertakan daftar instruksi dengan alamat yang perlu diperbarui untuk mencerminkan di mana memori dialokasikan (seperti gaya pertama), tetapi alih-alih meminta aplikasi ditambal oleh pemuat OS, aplikasi akan menyertakan cukup kode untuk menambal sendiri .

Keempat pendekatan memiliki kelebihan dan kekurangan. Namun dalam setiap kasus, kompiler akan mengkonsolidasikan sejumlah variabel statis sewenang-wenang menjadi sejumlah kecil permintaan memori, dan penghubung akan mengkonsolidasikan semua itu ke dalam sejumlah kecil alokasi konsolidasi. Meskipun suatu aplikasi harus menerima sepotong memori dari sistem operasi atau loader, itu adalah kompiler dan linker yang bertanggung jawab untuk mengalokasikan masing-masing bagian dari potongan besar itu ke semua variabel individu yang membutuhkannya.

supercat
sumber
13

Inti dari pertanyaan Anda adalah ini: "Bagaimana memori" dialokasikan "dalam file yang dikompilasi? Bukankah memori selalu dialokasikan dalam RAM dengan semua hal manajemen memori virtual? Bukankah alokasi memori menurut definisi konsep runtime?"

Saya pikir masalahnya adalah ada dua konsep berbeda yang terlibat dalam alokasi memori. Pada dasarnya, alokasi memori adalah proses di mana kita mengatakan "item data ini disimpan dalam potongan memori khusus ini". Dalam sistem komputer modern, ini melibatkan proses dua langkah:

  • Beberapa sistem digunakan untuk memutuskan alamat virtual di mana item akan disimpan
  • Alamat virtual dipetakan ke alamat fisik

Proses yang terakhir adalah murni waktu berjalan, tetapi yang pertama dapat dilakukan pada waktu kompilasi, jika data memiliki ukuran yang diketahui dan jumlah tetap dari mereka diperlukan. Inilah dasarnya cara kerjanya:

  • Kompiler melihat file sumber yang berisi baris yang terlihat sedikit seperti ini:

    int c;
  • Ini menghasilkan output untuk assembler yang menginstruksikan untuk menyimpan memori untuk variabel 'c'. Ini mungkin terlihat seperti ini:

    global _c
    section .bss
    _c: resb 4
  • Ketika assembler berjalan, ia menyimpan penghitung yang melacak offset setiap item dari awal 'segmen' memori (atau 'bagian'). Ini seperti bagian-bagian dari 'struct' yang sangat besar yang berisi semua yang ada di seluruh file yang tidak memiliki memori aktual yang dialokasikan padanya saat ini, dan bisa di mana saja. Ini mencatat dalam tabel yang _cmemiliki offset tertentu (katakanlah 510 byte dari awal segmen) dan kemudian menambah penghitungnya dengan 4, sehingga variabel tersebut berikutnya akan berada pada (misalnya) 514 byte. Untuk kode apa pun yang memerlukan alamat _c, itu hanya menempatkan 510 di file output, dan menambahkan catatan bahwa output membutuhkan alamat segmen yang berisi _cmenambahkannya nanti.

  • Linker mengambil semua file output assembler, dan memeriksanya. Ini menentukan alamat untuk setiap segmen sehingga mereka tidak akan tumpang tindih, dan menambahkan offset yang diperlukan sehingga instruksi masih merujuk ke item data yang benar. Dalam kasus memori yang tidak diinisialisasi seperti yang ditempati olehc(assembler diberitahu bahwa memori akan diinisialisasi oleh fakta bahwa kompiler meletakkannya di segmen '.bss', yang merupakan nama yang dicadangkan untuk memori tidak diinisialisasi), itu termasuk bidang header dalam outputnya yang memberitahu sistem operasi berapa banyak yang perlu dipesan. Ini mungkin dipindahkan (dan biasanya) tetapi biasanya dirancang untuk dimuat lebih efisien di satu alamat memori tertentu, dan OS akan mencoba memuatnya di alamat ini. Pada titik ini, kami memiliki ide yang cukup bagus tentang alamat virtual yang akan digunakan c.

  • Alamat fisik tidak akan benar-benar ditentukan sampai program berjalan. Namun, dari sudut pandang programmer, alamat fisik sebenarnya tidak relevan — kita bahkan tidak akan pernah tahu apa itu, karena OS biasanya tidak repot-repot memberi tahu siapa pun, alamat fisik dapat sering berubah (bahkan ketika program sedang berjalan), dan Tujuan utama dari OS adalah untuk abstrak ini.

Jules
sumber
9

Sebuah executable menggambarkan ruang apa yang dialokasikan untuk variabel statis. Alokasi ini dilakukan oleh sistem, saat Anda menjalankan executable. Jadi variabel statis 1kB Anda tidak akan meningkatkan ukuran executable dengan 1kB:

static char[1024];

Kecuali tentu saja Anda menentukan penginisialisasi:

static char[1024] = { 1, 2, 3, 4, ... };

Jadi, selain 'bahasa mesin' (yaitu instruksi CPU), executable berisi deskripsi tata letak memori yang diperlukan.

makna-hal
sumber
5

Memori dapat dialokasikan dengan banyak cara:

  • di tumpukan aplikasi (seluruh tumpukan dialokasikan untuk aplikasi Anda oleh OS saat program dimulai)
  • dalam tumpukan sistem operasi (sehingga Anda dapat mengambil lebih banyak dan lebih banyak)
  • di tumpukan sampah yang dikendalikan pengumpul sampah (sama seperti keduanya di atas)
  • pada stack (sehingga Anda bisa mendapatkan stack overflow)
  • dicadangkan dalam segmen kode / data biner Anda (dapat dieksekusi)
  • di tempat yang jauh (file, jaringan - dan Anda menerima pegangan bukan penunjuk ke memori itu)

Sekarang pertanyaan Anda adalah apa "memori yang dialokasikan pada waktu kompilasi". Jelas itu hanya ungkapan yang salah, yang seharusnya merujuk pada alokasi segmen biner atau alokasi tumpukan, atau dalam beberapa kasus bahkan ke alokasi tumpukan, tetapi dalam kasus itu alokasi disembunyikan dari mata programmer oleh panggilan konstruktor yang tidak terlihat. Atau mungkin orang yang mengatakan bahwa hanya ingin mengatakan bahwa memori tidak dialokasikan pada heap, tetapi tidak tahu tentang alokasi stack atau segmen. (Atau tidak ingin masuk ke detail seperti itu).

Tetapi dalam kebanyakan kasus orang hanya ingin mengatakan bahwa jumlah memori yang dialokasikan diketahui pada waktu kompilasi .

Ukuran biner hanya akan berubah ketika memori dicadangkan dalam kode atau segmen data aplikasi Anda.

exebook
sumber
1
Jawaban ini membingungkan (atau bingung) karena berbicara tentang "tumpukan aplikasi", "tumpukan OS", dan "tumpukan GC" seolah-olah semua ini adalah konsep yang bermakna. Saya menyimpulkan bahwa dengan # 1 Anda mencoba untuk mengatakan bahwa beberapa bahasa pemrograman mungkin (secara hipotetis) menggunakan skema "alokasi tumpukan" yang mengalokasikan memori keluar dari buffer ukuran tetap di bagian .data, tetapi tampaknya cukup tidak realistis sehingga berbahaya untuk pemahaman OP. Kembali # 2 dan # 3, kehadiran GC tidak benar-benar mengubah apa pun. Dan kembali # 5, Anda menghilangkan perbedaan yang jauh lebih penting antara .datadan .bss.
Quuxplusone
4

Kamu benar. Memori sebenarnya dialokasikan (halaman) pada waktu pengambilan, yaitu ketika file yang dapat dieksekusi dibawa ke memori (virtual). Memori juga dapat diinisialisasi pada saat itu. Kompiler hanya membuat peta memori. [Ngomong-ngomong, ruang stack dan heap juga dialokasikan pada waktu pengambilan!]

Yves Daoust
sumber
2

Saya pikir Anda perlu mundur sedikit. Memori yang dialokasikan pada waktu kompilasi .... Apa artinya itu? Dapatkah ini berarti bahwa memori pada chip yang belum diproduksi, untuk komputer yang belum dirancang, entah bagaimana sedang dipesan? Tidak. Tidak, perjalanan waktu, tidak ada kompiler yang dapat memanipulasi alam semesta.

Jadi, itu harus berarti bahwa kompiler menghasilkan instruksi untuk mengalokasikan memori itu entah bagaimana pada saat runtime. Tetapi jika Anda melihatnya dari sudut kanan, kompiler menghasilkan semua instruksi, jadi apa bedanya. Perbedaannya adalah bahwa kompilator memutuskan, dan pada saat runtime, kode Anda tidak dapat mengubah atau memodifikasi keputusannya. Jika diputuskan diperlukan 50 byte pada waktu kompilasi, saat runtime, Anda tidak dapat membuatnya memutuskan untuk mengalokasikan 60 - keputusan itu telah dibuat.

jmoreno
sumber
Saya suka jawaban yang menggunakan metode Socrates, tapi saya masih menurunkan Anda untuk kesimpulan yang salah bahwa "kompiler menghasilkan instruksi untuk mengalokasikan memori itu entah bagaimana pada saat runtime". Lihatlah jawaban pilihan teratas untuk melihat bagaimana kompiler dapat "mengalokasikan memori" tanpa menghasilkan "instruksi" runtime. (Perhatikan bahwa "instruksi" dalam konteks bahasa majelis memiliki arti tertentu, yaitu, opcode yang dapat dieksekusi. Anda mungkin menggunakan kata sehari-hari untuk mengartikan sesuatu seperti "resep", tetapi dalam konteks ini yang hanya akan membingungkan OP. )
Quuxplusone
1
@Quuxplusone: Saya membaca (dan meningkatkan) jawaban itu. Dan tidak, jawaban saya tidak secara khusus membahas masalah variabel yang diinisialisasi. Itu juga tidak membahas kode modifikasi diri. Meskipun jawaban itu sangat bagus, itu tidak membahas apa yang saya anggap sebagai masalah penting - meletakkan segala sesuatu dalam konteks. Oleh karena itu jawaban saya, yang saya harap akan membantu OP (dan lainnya) berhenti dan berpikir tentang apa yang sedang atau sedang terjadi, ketika mereka memiliki masalah yang tidak mereka pahami.
jmoreno
@Quuxplusone: Maaf jika saya membuat tuduhan palsu di sini, tapi saya menganggap Anda adalah salah satu dari orang-orang yang -1-jawaban saya juga. Jika demikian, maukah Anda menunjukkan bagian mana dari jawaban saya yang merupakan alasan utama untuk melakukannya, dan apakah Anda juga mau memeriksa edit saya? Saya tahu saya telah melewatkan beberapa bit tentang internal sebenarnya tentang bagaimana memori stack dikelola, jadi saya sekarang menambahkan sedikit tentang saya tidak 100% akurat untuk jawaban saya sekarang, lagian :)
Elias Van Ootegem
@ jmoreno Poin yang Anda buat tentang "Bisakah ini berarti bahwa memori pada chip yang belum diproduksi, untuk komputer yang belum dirancang, entah bagaimana sedang dipesan? Tidak." persis arti salah yang menyiratkan kata "alokasi" yang membingungkan saya sejak awal. Saya suka jawaban ini karena merujuk pada masalah yang saya coba tunjukkan. Tidak ada jawaban di sini yang benar-benar menyentuh poin itu. Terima kasih.
Talha Sayed
2

Jika Anda mempelajari pemrograman rakitan, Anda akan melihat bahwa Anda harus mengukir segmen untuk data, tumpukan, dan kode, dll. Bagian data adalah tempat string dan angka Anda tinggal. Segmen kode adalah tempat kode Anda tinggal. Segmen ini dibangun ke dalam program yang dapat dieksekusi. Tentu saja ukuran tumpukan juga penting ... Anda tidak ingin tumpukan meluap !

Jadi, jika segmen data Anda adalah 500 byte, program Anda memiliki area 500 byte. Jika Anda mengubah segmen data ke 1500 byte, ukuran program akan lebih besar 1000 byte. Data dikumpulkan ke dalam program yang sebenarnya.

Inilah yang terjadi ketika Anda mengkompilasi bahasa tingkat yang lebih tinggi. Area data aktual dialokasikan ketika dikompilasi ke dalam program yang dapat dieksekusi, meningkatkan ukuran program. Program ini dapat meminta memori dengan cepat, dan ini adalah memori dinamis. Anda dapat meminta memori dari RAM dan CPU akan memberikannya kepada Anda untuk digunakan, Anda dapat melepaskannya, dan pengumpul sampah Anda akan melepaskannya kembali ke CPU. Bahkan dapat ditukar ke hard disk, jika perlu, oleh manajer memori yang baik. Fitur-fitur ini adalah apa yang disediakan bahasa tingkat tinggi bagi Anda.

Insinyur
sumber
2

Saya ingin menjelaskan konsep-konsep ini dengan bantuan beberapa diagram.

Ini benar bahwa memori tidak dapat dialokasikan pada waktu kompilasi, pasti. Tapi, lalu apa yang terjadi sebenarnya pada waktu kompilasi.

Di sinilah penjelasannya. Katakanlah, misalnya suatu program memiliki empat variabel x, y, z dan k. Sekarang, pada waktu kompilasi itu hanya membuat peta memori, di mana lokasi variabel-variabel ini terhadap satu sama lain dipastikan. Diagram ini akan menggambarkannya dengan lebih baik.

Sekarang bayangkan, tidak ada program yang berjalan di memori. Ini saya tunjukkan dengan kotak kosong besar.

bidang kosong

Selanjutnya, instance pertama dari program ini dieksekusi. Anda dapat memvisualisasikannya sebagai berikut. Ini adalah waktu ketika sebenarnya memori dialokasikan.

contoh pertama

Ketika instance kedua dari program ini berjalan, memori akan terlihat seperti berikut.

contoh kedua

Dan yang ketiga ..

contoh ketiga

Begitu seterusnya dan seterusnya.

Saya berharap visualisasi ini menjelaskan konsep ini dengan baik.

pengguna3258051
sumber
2
Jika diagram tersebut menunjukkan perbedaan antara memori statis dan dinamis, mereka akan menjadi IMHO yang lebih berguna.
Bartek Banachewicz
Ini sengaja saya hindari untuk menjaga hal-hal sederhana. Fokus saya adalah menjelaskan funda ini dengan jelas tanpa banyak kekacauan teknis. Sejauh ini dimaksudkan untuk variabel statis .. Poin ini telah ditetapkan dengan baik oleh jawaban sebelumnya. Jadi saya melewatkan ini.
user3258051
1
Eh, konsep ini tidak terlalu rumit, jadi saya tidak mengerti mengapa membuatnya lebih sederhana daripada yang seharusnya, tetapi karena itu hanya dimaksudkan sebagai jawaban pelengkap, ok.
Bartek Banachewicz