Kapan saya harus menggunakan malloc di C dan kapan tidak?

94

Saya mengerti cara kerja malloc (). Pertanyaan saya adalah, saya akan melihat hal-hal seperti ini:

#define A_MEGABYTE (1024 * 1024)

char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);

Saya menghilangkan pengecekan kesalahan demi singkatnya. Pertanyaan saya adalah, tidak bisakah Anda melakukan hal di atas dengan menginisialisasi pointer ke beberapa penyimpanan statis di memori? mungkin:

char *some_memory = "Hello World";

Pada titik manakah Anda benar-benar perlu mengalokasikan memori sendiri daripada mendeklarasikan / menginisialisasi nilai yang perlu Anda pertahankan?

randombits
sumber
5
Perihal: Saya menghilangkan pemeriksaan kesalahan demi singkatnya - sayangnya terlalu banyak pemrogram yang mengabaikan pemeriksaan kesalahan karena mereka tidak menyadari malloc()dapat gagal!
Andrew

Jawaban:

133
char *some_memory = "Hello World";

membuat pointer ke konstanta string. Itu berarti string "Hello World" akan berada di suatu tempat di bagian memori hanya-baca dan Anda hanya memiliki penunjuk ke sana. Anda dapat menggunakan string sebagai baca-saja. Anda tidak dapat mengubahnya. Contoh:

some_memory[0] = 'h';

Apakah mencari masalah.

Di samping itu

some_memory = (char *)malloc(size_to_allocate);

mengalokasikan array char (variabel) dan some_memory menunjuk ke memori yang dialokasikan. Sekarang array ini membaca dan menulis. Sekarang Anda dapat melakukan:

some_memory[0] = 'h';

dan konten array berubah menjadi "hello World"

codaddict
sumber
19
Hanya untuk memperjelas, sebanyak saya suka jawaban ini (saya memang memberi Anda +1), Anda dapat melakukan hal yang sama tanpa malloc () hanya dengan menggunakan array karakter. Sesuatu seperti: char some_memory [] = "Halo"; some_memory [0] = 'W'; juga akan bekerja.
randombits
19
Anda benar. Kamu bisa melakukannya. Ketika Anda menggunakan malloc () memori dialokasikan secara dinamis pada waktu proses, jadi Anda tidak perlu memperbaiki ukuran array pada waktu kompilasi juga Anda dapat membuatnya bertambah atau berkurang menggunakan realloc () Tak satu pun dari hal-hal ini dapat dilakukan saat Anda melakukannya: char some_memory [] = "Halo"; Di sini meskipun Anda dapat mengubah konten larik, ukurannya tetap. Jadi tergantung pada kebutuhan Anda, Anda menggunakan salah satu dari tiga opsi: 1) pointer ke char const 2) array yang dialokasikan secara dinamis 3) ukuran tetap, kompilasi waktu yang dialokasikan array.
codaddict
Untuk menekankan itu read-only Anda harus menulis const char *s = "hi";Bukankah ini sebenarnya dipersyaratkan oleh standar?
Sampai Theis
1
@Sampai, tidak karena Anda mendeklarasikan pointer yang diinisialisasi ke alamat dasar string literal "hi". s dapat ditugaskan kembali secara legal untuk menunjuk ke karakter non-const. Jika Anda menginginkan penunjuk konstan ke string hanya baca, Anda perluconst char const* s;
Rob11311
38

Untuk contoh yang tepat, malloc tidak banyak berguna.

Alasan utama malloc diperlukan adalah ketika Anda memiliki data yang harus memiliki masa pakai yang berbeda dari cakupan kode. Kode Anda memanggil malloc dalam satu rutinitas, menyimpan penunjuk di suatu tempat dan akhirnya menelepon gratis di rutinitas yang berbeda.

Alasan kedua adalah bahwa C tidak memiliki cara untuk mengetahui apakah ada cukup ruang tersisa di tumpukan untuk alokasi. Jika kode Anda harus 100% kuat, lebih aman menggunakan malloc karena kode Anda dapat mengetahui alokasi gagal dan menanganinya.

R Samuel Klatchko
sumber
4
Siklus hidup memori, dan pertanyaan terkait kapan dan bagaimana membatalkan alokasinya, merupakan masalah penting dengan banyak pustaka umum dan komponen perangkat lunak. Mereka biasanya memiliki aturan terdokumentasi dengan baik: "Jika Anda melewatkan pointer ke ini salah satu rutinitas saya, Anda harus telah malloc'd itu saya akan melacak itu, dan bebas itu ketika saya sudah selesai dengan itu.. " Sumber umum bug jahat adalah meneruskan pointer ke memori yang dialokasikan secara statis ke library semacam itu. Saat perpustakaan mencoba membebaskannya (), program lumpuh. Saya baru-baru ini menghabiskan banyak waktu untuk memperbaiki bug seperti yang ditulis orang lain.
Bob Murphy
Apakah Anda mengatakan bahwa satu-satunya waktu malloc () digunakan secara praktis, adalah ketika ada segmen kode yang akan dipanggil beberapa kali selama masa program yang akan dipanggil beberapa kali dan perlu 'dibersihkan', karena malloc () disertai dengan free ()? Misalnya, dalam permainan seperti roda keberuntungan, di mana setelah Anda menebak, dan memasukkan input ke dalam array karakter yang ditentukan, bahwa array berukuran malloc () dapat dibebaskan untuk tebakan berikutnya?
Smith Will Suffice
Masa pakai data memang alasan sebenarnya untuk menggunakan malloc. Misalkan tipe data abstrak diwakili oleh modul, itu mendeklarasikan tipe Daftar, dan rutinitas untuk menambah / menghapus item dari daftar. Nilai item tersebut, perlu disalin ke dalam memori yang dialokasikan secara dinamis.
Rob11311
@Bob: bug jahat itu, buat konvensi bahwa pengalokasi membebaskan memori jauh lebih unggul, setelah semua Anda dapat mendaur ulangnya. Misalkan Anda mengalokasikan memori dengan calloc untuk meningkatkan lokalitas referensi, yang memperlihatkan sifat rusak pustaka tersebut, karena Anda hanya perlu memanggil gratis sekali untuk seluruh blok. Untungnya saya tidak harus menggunakan pustaka yang menetapkan memori menjadi 'malloc-ed`, ini bukan tradisi POSIX dan kemungkinan besar akan dianggap sebagai bug. Jika mereka "tahu" Anda harus menggunakan malloc, mengapa rutinitas perpustakaan tidak melakukannya untuk Anda?
Rob11311
17

malloc adalah alat yang luar biasa untuk mengalokasikan, mengalokasikan ulang, dan membebaskan memori saat runtime, dibandingkan dengan deklarasi statis seperti contoh hello world Anda, yang diproses pada waktu kompilasi dan karenanya tidak dapat diubah ukurannya.

Oleh karena itu, Malloc selalu berguna saat Anda menangani data berukuran arbitrer, seperti membaca konten file atau berurusan dengan soket dan Anda tidak mengetahui panjang data yang akan diproses.

Tentu saja, dalam contoh sepele seperti yang Anda berikan, malloc bukanlah "alat yang tepat untuk pekerjaan yang tepat", tetapi untuk kasus yang lebih kompleks (misalnya membuat larik berukuran arbitrer pada waktu proses), ini adalah satu-satunya cara untuk Pergilah.

moritz
sumber
7

Jika Anda tidak mengetahui ukuran pasti dari memori yang perlu Anda gunakan, Anda memerlukan alokasi dinamis ( malloc). Contohnya mungkin ketika pengguna membuka file di aplikasi Anda. Anda perlu membaca konten file ke dalam memori, tetapi tentu saja Anda tidak mengetahui ukuran file sebelumnya, karena pengguna memilih file tersebut di tempat, saat runtime. Jadi pada dasarnya Anda perlu mallocketika Anda tidak mengetahui ukuran data yang Anda kerjakan sebelumnya. Setidaknya itulah salah satu alasan utama penggunaan malloc. Dalam contoh Anda dengan string sederhana yang sudah Anda ketahui ukurannya pada waktu kompilasi (ditambah Anda tidak ingin mengubahnya), tidak masuk akal untuk mengalokasikannya secara dinamis.


Sedikit keluar dari topik, tapi ... Anda harus sangat berhati-hati agar tidak menyebabkan kebocoran memori saat menggunakan malloc. Pertimbangkan kode ini:

int do_something() {
    uint8_t* someMemory = (uint8_t*)malloc(1024);

    // Do some stuff

    if ( /* some error occured */ ) return -1;

    // Do some other stuff

    free(someMemory);
    return result;
}

Apakah Anda melihat apa yang salah dengan kode ini? Ada pernyataan pengembalian bersyarat antara mallocdanfree . Awalnya mungkin tampak baik-baik saja, tetapi pikirkanlah. Jika ada kesalahan, Anda akan kembali tanpa membebaskan memori yang Anda alokasikan. Ini adalah sumber kebocoran memori yang umum.

Tentu saja ini adalah contoh yang sangat sederhana, dan sangat mudah untuk melihat kesalahannya di sini, tetapi bayangkan ratusan baris kode berserakan dengan pointer, mallocs,free s, dan segala macam penanganan kesalahan. Segalanya bisa menjadi sangat berantakan dengan sangat cepat. Ini adalah salah satu alasan saya lebih memilih C ++ modern daripada C dalam kasus yang berlaku, tetapi itu topik yang sama sekali berbeda.

Jadi, kapan pun Anda menggunakan malloc, selalu pastikan memori Anda freesedapat mungkin.

adam10603
sumber
Contoh yang bagus! Cara untuk pergi ^ _ ^
Musa Al-hassy
6
char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");

ilegal, string literal adalah ilegal const.

Ini akan mengalokasikan array karakter 12-byte pada stack atau secara global (tergantung di mana ia dideklarasikan).

char some_memory[] = "Hello World";

Jika Anda ingin meninggalkan ruang untuk manipulasi lebih lanjut, Anda dapat menentukan bahwa array harus berukuran lebih besar. (Tolong jangan menaruh 1MB di tumpukan.)

#define LINE_LEN 80

char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);
efemient
sumber
5

Salah satu alasan saat perlu mengalokasikan memori adalah jika Anda ingin mengubahnya saat runtime. Dalam hal ini, malloc atau buffer di stack dapat digunakan. Contoh sederhana dari menugaskan "Hello World" ke pointer mendefinisikan memori yang "biasanya" tidak dapat dimodifikasi saat runtime.

Mark Wilkins
sumber