Mengalokasikan memori dengan cepat adalah cara program C tumbuh dan menyusut saat dijalankan. Anda tidak mengetahui kebutuhan pasti aplikasi Anda sampai aplikasi tersebut benar-benar dijalankan. Solusinya adalah tumpukan. Anda meminta pemblokiran. Sistem operasi menyimpannya. Anda menggunakannya. Kemudian Anda mengembalikannya dengan gratis. Siklus ini memungkinkan program lain mendaur ulang RAM yang sama.
Perhatikan contoh sederhana ini. Ini adalah interaksi paling dasar dengan heap yang bisa Anda dapatkan.
Cara Kerja Malloc Sebenarnya
Fungsi malloc bukanlah sihir. Itu adalah mekanik yang memeriksa inventaris. Saat Anda menyebutnya, tiga hal terjadi secara berurutan.
- Ia memeriksa kapasitas heap saat ini. Ini terlihat pada parameter yang Anda lewati—dalam hal ini
sizeof(int), yaitu 4 byte. Ia menanyakan apakah ada cukup ruang yang berdekatan. Jika heap terfragmentasi atau penuh, maka gagal. - Jika ada ruang, blok tersebut dicadangkan. Sistem menandainya sebagai “milik Anda”. Tidak ada panggilan
malloclain yang dapat menyentuh bagian ini. Ini mencegah tabrakan yang tidak disengaja antar alokasi. - Ia mengembalikan alamat blok itu. Itu tidak memberi Anda data itu sendiri. Ini memberi Anda variabel penunjuk. Variabel itu menampung lokasi. Anda sekarang memiliki pegangan untuk tempat tertentu di RAM.
Dalam contoh kita, p menerima alamat itu. Variabel pointer hanyalah sebuah wadah untuk lokasi memori. Nilai bilangan bulat sebenarnya ada di lokasi itu.
Mengapa Anda Harus Memeriksa NULL
Anda melihat tanda centang if (p == 0) segera setelah alokasi. Beberapa pemula melewatkan ini. Ini adalah kesalahan fatal.
Selalu periksa penunjuk setelah panggilan apa pun ke malloc untuk memastikan penunjuk tersebut valid.
Ukuran heap berfluktuasi secara konstan. Itu tergantung pada proses lain yang sedang berjalan. Itu tergantung pada seberapa banyak memori yang mereka ambil. Tidak ada jaminan panggilan akan berhasil. Jika malloc gagal, ia mengembalikan nol. Nol adalah NULL. Jika Anda mencoba melakukan dereferensi penunjuk nol, program Anda akan mogok. Keras.
Anda dapat menulis if (p == NULL) atau if (!p). Maksudnya sama. Tapi Anda harus memeriksanya. Setiap saat.
Apa Yang Terjadi Jika Anda Lupa Mengosongkan Memori?
Jika program Anda berakhir dan Anda tidak memanggil gratis, sistem operasi akan membersihkannya. Ini melepaskan ruang yang dapat dieksekusi. Ini membersihkan tumpukan. Ini merebut kembali memori global. Itu juga mendaur ulang alokasi heap yang tertunda.
Jadi, bolehkah membocorkan memori di akhir program? Secara teknis, ya. OS menangani pembersihan. Tidak ada konsekuensi jangka panjang pada mesin.
Tapi itu bentuk yang buruk. Dan yang lebih penting, kebocoran memori selama eksekusi berakibat fatal. Jika Anda terus mengalokasikan tanpa membebaskan, Anda membuat proses Anda sendiri kelaparan. Tumpukannya terisi. Alokasi baru gagal. Program ini mati karena mati lemas, bukan karena kecelakaan.
Petunjuk vs. Nilai yang Ditunjuknya
Petunjuk bisa membingungkan. Nilai-nilai tersebut berbeda dengan nilai-nilai yang dirujuknya. Inilah bagaimana hal itu terjadi dalam praktiknya.
Skenario pertama. Anda mengalokasikan satu blok. Anda menunjukkan dua variabel padanya.
Keluaran:
10
20
Mengapa? p dan q adalah petunjuk yang berbeda. Namun q = p membuatnya menunjuk ke blok memori yang sama. Mengubah nilai melalui p akan mengubahnya menjadi q. Mengubahnya melalui q akan mengubahnya menjadi p. Itu adalah dua pegangan di pintu yang sama.
Skenario kedua. Anda mengalokasikan dua blok terpisah.
Keluaran:
20
Di sini, p dan q menunjuk ke lokasi yang berbeda. *q menampung 20. *p = *q menyalin nilai 20 ke lokasi yang ditunjuk p. Penunjuk p itu sendiri tidak bergerak. Nilai pada alamat p berubah.
Perbedaan itu penting. Seseorang mengubah alamat yang disimpan dalam variabel. Yang lain mengubah data di alamat itu. Membingungkan keduanya akan menyebabkan kesalahan logika yang lebih sulit untuk di-debug dibandingkan kesalahan sintaksis.


Pointer, Memori, dan Terjun Bebas
Outputnya adalah 20. Baris 6 menjelaskan semuanya.
Anda mungkin berpikir *p = *q dan p = q melakukan hal yang sama. Mereka tidak melakukannya. Seseorang memindahkan data. Yang lainnya memindahkan alamat. Kompiler memungkinkan Anda menetapkan *p = *q karena kedua pointer yang direferensikan adalah bilangan bulat. Ini adalah pertukaran nilai secara langsung. Tapi p = q? Itu berbeda. Ini menunjuk p ke blok memori yang sama persis dengan q. Alamatnya berpindah. Datanya tetap ada.
Jika tipenya tidak cocok, kompiler akan memblokirnya. Penunjuk ke bilangan bulat tidak bisa menunjuk ke string karakter. Jenisnya harus selaras.
Empat Cara Inisialisasi
Variabel penunjuk baru adalah liabilitas hingga diinisialisasi.
int *p; membuat penunjuk yang tidak menunjuk ke tempat tertentu. Ini menunjuk pada sampah. Dereferensi itu adalah sebuah kesalahan. Anda memerlukan lokasi yang diketahui. Ada empat cara untuk sampai ke sana.
- malloc : Ini mengalokasikan satu blok di heap.
psekarang memiliki alamat yang valid. Pointer diinisialisasi karena menunjuk ke bagian memori tertentu yang dicadangkan. - Penugasan :
p = q;jikaqvalid,pmenjadi valid. Ini mewarisi alamatq. Jikaqadalah sampah,pjuga mendapat sampah. - Alamat Eksplisit : Anda dapat mengarahkan
plangsung ke variabel yang diketahui.p = &i;menunjukpke bilangan bulati. Sederhana. Langsung. - Nol/NULL :
p = 0;ataup = NULL;. Ini menetapkan alamat menjadi nol. Itu tidak menunjuk pada satu blok. Ini menunjuk ke lokasi nol.
Mengapa menggunakan nol? Itu sebuah bendera.
Anda dapat memeriksa if (p == 0) untuk melihat apakah penunjuknya valid. Sistem mengakui hal ini. Jika Anda mencoba melakukan dereferensi penunjuk nol, program akan mogok. Keras.
p = 0; *p = 5;
Baris kedua gagal. p tidak menunjukkan apa-apa. Anda tidak dapat menulis apa pun. Perilaku ini sangat penting untuk daftar tertaut di kemudian hari. Ini adalah jaring pengaman.
Membebaskan Tumpukan
Mengalokasikan memori saja tidak cukup. Anda harus mengembalikannya.
malloc mengambil ruang. gratis mengembalikannya.
free(p) melakukan dua hal. Pertama, ia membatalkan blok tersebut. Tumpukan mengembalikan memori itu. Itu bisa digunakan kembali. Kedua, p dibiarkan tidak diinisialisasi. Ini tidak secara ajaib menjadi aman. Anda harus menginisialisasi ulang p sebelum menggunakannya lagi.
Blok tersebut hilang dari jangkauan Anda. Penunjuknya menjuntai.
Berikut adalah pola standar untuk menangani bilangan bulat tunggal di heap:
Ini sebagian besar adalah demo. Tapi itu menunjukkan siklus hidupnya.
sizeof(int) mengembalikan ukuran dalam byte. Di sebagian besar mesin, jumlahnya 4. Anda dapat melakukan hardcode malloc(4). Jangan. sizeof membuat kode menjadi portabel. Bacaannya lebih baik. Ini beradaptasi jika arsitektur berubah.
malloc mengembalikan penunjuk umum. Kompiler sering kali memperingatkan jika Anda tidak mentransmisikannya. (int *) mengonversi gumpalan generik tersebut menjadi penunjuk ke bilangan bulat. Ini cocok dengan p.
free(p) mengembalikan blok ke heap.
Struktur dan Prioritas Operator
Struct berperilaku dengan cara yang sama. Anda mengalokasikan satu blok untuk keseluruhan struktur, bukan hanya bidang.
Lihatlah (*p).i = 10;.
Mengapa tanda kurung?
Karena . memiliki prioritas lebih tinggi daripada *.
Tanpa tanda kurung, *p.i berarti *(p.i). Ia mencoba mengakses i pada penunjuk p, lalu melakukan dereferensi itu. Bukan itu yang Anda inginkan. Anda ingin melakukan dereferensi p terlebih dahulu, lalu mengakses i.
Aturan prioritas sangat ketat. * mengikat lebih erat daripada . dalam konteks ini. Tanda kurung memaksa dereferensi terjadi sebelum anggota mengakses.
Kebanyakan orang benci mengetik (*p).i berulang kali. Itu bertele-tele.
C memiliki singkatan.
p->i
Ini persis sama dengan (*p).i. Tapi lebih cepat untuk mengetik. Anda akan melihat -> di mana saja dalam kode C. Ini adalah cara standar untuk mengakses anggota struct melalui sebuah pointer.
Heap adalah sumber daya yang terbatas. Alokasikan dengan hati-hati. Batalkan alokasi secara konsisten. Atau lihat ingatan Anda hilang.

























