Setiap variabel yang kita deklarasikan harus tinggal di suatu tempat di memori, dan di C++ kita diberi dua wilayah utama untuk itu: stack dan heap. Stack adalah wilayah yang dikelola otomatis oleh compiler — cepat, rapi, tetapi ukuran dan umurnya terikat pada blok kode tempat variabel berada. Heap adalah wilayah yang fleksibel: kita bisa memesan memori sebesar apa pun kapan pun, tetapi pembongkarannya menjadi tanggung jawab kita atau pustaka yang kita pakai. Hampir semua topik pada Section ini — new/delete, memory leak, RAII, dan smart pointer — pada akhirnya bermuara pada perbedaan dua wilayah ini. Mari kita mulai dengan memahami karakter keduanya secara utuh.
Stack: Memori yang Dikelola Otomatis #
Stack adalah wilayah memori yang bekerja dengan pola LIFO (last in, first out): yang terakhir masuk, yang pertama keluar. Setiap kali sebuah function dipanggil, runtime menyisipkan satu stack frame di ujung stack — di dalamnya ada parameter, variabel lokal, dan alamat kembali. Begitu function kembali, seluruh frame-nya dicabut sekaligus, dan semua variabel lokal di dalamnya berakhir secara otomatis. Karena itulah kita tidak pernah perlu “melepas” variabel lokal di C++.
- Otomatis: pembuatan dan pembongkaran variabel lokal ditangani compiler tanpa satu baris kode pun dari kita.
- Sangat cepat: alokasi hanya berarti menggeser satu penunjuk ujung stack; tidak ada pencarian ruang kosong.
- Terbatas: ukuran stack kecil dibanding heap — pada Linux umumnya sekitar 8 MB per thread. Rekursi yang terlalu dalam bisa melebihi kapasitasnya dan menghentikan program.
Heap: Memori yang Kita Kelola Sendiri #
Heap adalah wilayah memori bebas yang jauh lebih luas, dikelola oleh alokator memori milik runtime C++. Ketika kita meminta ruang — lewat operator new, lewat smart pointer, atau lewat container seperti std::vector — alokator mencari blok kosong yang cukup, mencatatnya di pembukuan internal, lalu menyerahkan alamatnya kepada kita. Umur memori heap tidak terikat pada blok mana pun: objek yang dialokasikan di heap tetap hidup sampai secara eksplisit dilepas. Fleksibilitas ini berbayar: alokasi lebih lambat daripada stack karena ada pencarian blok dan pembukuan yang harus dikelola, dan kalau kita lupa melepas, memorinya bocor tanpa bunyi.
Diagram di atas memperlihatkan hubungan keduanya: objek std::vector sendiri berada di stack karena ia variabel lokal, tetapi elemen-elemen yang ia simpan berada di heap. Vector adalah contoh sempurna pembagian kerja ini — kita memakai kemudahan stack untuk objek pengelolanya, sementara vector yang berperan mengelola heap untuk kita.
Membuktikan Dua Wilayah dalam Satu Program #
Program berikut menempatkan satu variabel biasa di stack dan satu vector di atas pasangan stack-heap. Output-nya sederhana, tetapi mekanismenya adalah ringkasan dari dua wilayah yang baru kita bahas.
#include <iostream>
#include <vector>
int main() {
// Wilayah 1: stack — variabel lokal, dikelola otomatis
int angka = 42;
std::cout << "angka (di stack): " << angka << "\n";
// Wilayah 2: heap — objek vector di stack, elemennya di heap
std::vector<int> data = {10, 20, 30};
data.push_back(40); // elemen baru dialokasikan di heap oleh vector
std::cout << "elemen (di heap):";
for (int x : data) {
std::cout << " " << x;
}
std::cout << "\n";
std::cout << "jumlah elemen : " << data.size() << "\n";
// Setelah main() selesai: vector membebaskan elemen heap-nya sendiri,
// dan angka ikut berakhir bersama blok fungsi.
return 0;
}Output yang Dihasilkan:
angka (di stack): 42
elemen (di heap): 10 20 30 40
jumlah elemen : 4Mari kita telaah baris-baris pentingnya:
- Baris 6 (
int angka = 42;):angkaadalah variabel lokalmain(), sehingga ia disimpan di stack frame milikmain(). Tidak ada kode pelepasan — ia berakhir otomatis. - Baris 10 (
std::vector<int> data = {10, 20, 30};): objekdataberada di stack, tetapi vector langsung memesan ruang di heap untuk menampung ketiga elemennya. - Baris 11 (
data.push_back(40);): vector meminta alokasi tambahan di heap saat kapasitasnya tidak cukup. Kita tidak melihat satunewpun — vector yang melakukannya untuk kita. - Baris 13-17 (loop cetak): membaca elemen berarti membaca memori heap lewat antarmuka yang aman dari vector.
- Baris 22 (
return 0;): saatmain()berakhir, destructor vector dijalankan dan seluruh elemen heap-nya dibebaskan otomatis.
Kapan Memakai yang Mana #
Aturan praktisnya dapat diringkas dalam tabel berikut:
| Kebutuhan | Wilayah | Alat yang tepat |
|---|---|---|
| Nilai kecil berumur sesuai blok | Stack | Variabel lokal biasa |
| Data besar atau ukurannya baru tahu saat runtime | Heap | std::vector, std::string |
| Objek yang harus hidup melebihi scope-nya | Heap | Smart pointer (Chapter unique_ptr dan setelahnya) |
| Data milik bersama beberapa pemilik | Heap | std::shared_ptr |
Peta dua wilayah ini masih setengah cerita: kapan tepatnya sebuah variabel “lahir” dan “berakhir” ditentukan oleh storage duration. Chapter berikutnya membandingkan automatic storage dan dynamic storage — dua istilah standar untuk umur memori yang sudah kita rasakan di atas.
Referensi: cppreference — std::vector , cppreference — storage duration .