Saya menggunakan kode berikut dalam aplikasi saya, dan itu berfungsi dengan baik. Tapi saya bertanya-tanya apakah lebih baik membuatnya dengan malloc atau membiarkannya apa adanya?
function (int len)
{
char result [len] = some chars;
send result over network
}
Jawaban:
Perbedaan utama adalah bahwa VLA (array panjang variabel) tidak menyediakan mekanisme untuk mendeteksi kegagalan alokasi.
Jika Anda menyatakan
dan
len
melebihi jumlah ruang tumpukan yang tersedia, perilaku program Anda tidak ditentukan. Tidak ada mekanisme bahasa baik untuk menentukan terlebih dahulu apakah alokasi akan berhasil, atau untuk menentukan setelah fakta apakah itu berhasil.Di sisi lain, jika Anda menulis:
maka Anda dapat menangani kegagalan dengan anggun, atau setidaknya menjamin bahwa program Anda tidak akan mencoba untuk terus mengeksekusi setelah kegagalan.
(Yah, sebagian besar. Pada sistem Linux,
malloc()
dapat mengalokasikan sejumlah ruang alamat bahkan jika tidak ada penyimpanan yang sesuai tersedia; kemudian upaya untuk menggunakan ruang tersebut dapat memanggil Pembunuh OOM . Tetapi memeriksamalloc()
kegagalan masih merupakan praktik yang baik.)Masalah lain, pada banyak sistem, adalah bahwa ada lebih banyak ruang (mungkin lebih banyak ruang) tersedia
malloc()
daripada untuk objek otomatis seperti VLA.Dan seperti jawaban Philip sudah disebutkan, VLA ditambahkan dalam C99 (Microsoft khususnya tidak mendukung mereka).
Dan VLA dibuat opsional di C11. Mungkin sebagian besar kompiler C11 akan mendukungnya, tetapi Anda tidak dapat mengandalkannya.
sumber
Array otomatis panjang variabel diperkenalkan ke C di C99.
Kecuali jika Anda memiliki kekhawatiran tentang keterbandingan mundur dengan standar yang lebih lama, tidak masalah.
Secara umum, jika berhasil, jangan menyentuhnya. Jangan mengoptimalkan sebelumnya. Jangan khawatir tentang menambahkan fitur khusus atau cara pintar dalam melakukan sesuatu, karena Anda sering tidak akan menggunakannya. Tetap sederhana.
sumber
Jika kompiler Anda mendukung array panjang variabel, satu-satunya bahaya adalah meluap tumpukan pada beberapa sistem, ketika
len
itu sangat besar. Jika Anda tahu pasti bahwalen
itu tidak akan lebih besar dari angka tertentu, dan Anda tahu bahwa tumpukan Anda tidak akan meluap bahkan pada panjang maksimal, biarkan kode seperti apa adanya; jika tidak, tulis ulang denganmalloc
danfree
.sumber
char result [sizeof(char)]
adalah array ukuran1
(karenasizeof(char)
sama dengan satu), jadi tugasnya akan terpotongsome chars
.str
meluruh ke pointer , jadisizeof
itu akan menjadi empat atau delapan, tergantung pada ukuran pointer pada sistem Anda.char* result = alloca(len);
, yang mengalokasikan pada stack. Ini memiliki efek dasar yang sama (dan masalah dasar yang sama)Saya suka ide bahwa Anda dapat memiliki array run-time yang dialokasikan tanpa fragmentasi memori, pointer menggantung, dll. Namun, yang lain telah menunjukkan bahwa alokasi run-time ini bisa gagal gagal. Jadi saya mencoba ini menggunakan gcc 4.5.3 di lingkungan bash Cygwin:
Outputnya adalah:
Panjang yang terlalu besar yang dilewati pada panggilan kedua jelas menyebabkan kegagalan (meluap ke penanda []). Ini tidak berarti bahwa cek semacam ini adalah bukti bodoh (bodoh bisa pintar!) Atau bahwa itu memenuhi standar C99, tetapi mungkin membantu jika Anda memiliki kekhawatiran itu.
Seperti biasa, YMMV.
sumber
Secara umum tumpukan adalah tempat termudah dan terbaik untuk menaruh data Anda.
Saya akan menghindari masalah VLA dengan hanya mengalokasikan array terbesar yang Anda harapkan.
Namun ada beberapa kasus ketika tumpukan terbaik dan bermain-main dengan malloc layak dilakukan.
sumber
Dalam pemrograman tertanam, kami selalu menggunakan array statis alih-alih malloc ketika malloc dan operasi bebas sering dilakukan. Karena kurangnya manajemen memori dalam sistem embedded, alokasi yang sering dan operasi bebas akan menyebabkan fragmen memori. Tetapi kita harus menggunakan beberapa metode rumit seperti mendefinisikan ukuran maksimum array dan menggunakan array lokal statis.
Jika aplikasi Anda berjalan di Linux atau Windows, tidak masalah menggunakan array atau malloc. Poin kuncinya adalah di mana Anda menggunakan struktur tanggal dan logika kode Anda.
sumber
Sesuatu yang belum ada yang disebutkan adalah bahwa opsi array panjang variabel mungkin akan jauh lebih cepat daripada malloc / gratis karena mengalokasikan VLA hanyalah kasus menyesuaikan stack pointer (setidaknya di GCC).
Jadi, jika fungsi ini adalah yang sering dipanggil (yang tentunya akan Anda tentukan dengan membuat profil), VLA adalah opsi pengoptimalan yang baik.
sumber
Ini adalah solusi C yang sangat umum saya gunakan untuk masalah yang mungkin bisa membantu. Tidak seperti VLA, VLA tidak menghadapi risiko praktis stack overflow dalam kasus patologis.
Untuk menggunakannya dalam kasus Anda:
Apa yang dilakukan dalam kasus di atas adalah menggunakan stack jika string cocok menjadi 512 byte atau kurang. Kalau tidak, ia menggunakan alokasi tumpukan. Ini dapat berguna jika, katakanlah, 99% dari waktu, string tersebut cocok menjadi 512 byte atau kurang. Namun, katakanlah ada beberapa kasus eksotis gila yang kadang-kadang Anda mungkin perlu menangani di mana string adalah 32 kilobyte di mana pengguna tertidur di keyboard-nya atau sesuatu seperti itu. Ini memungkinkan kedua situasi ditangani tanpa masalah.
Versi aktual yang saya gunakan dalam produksi juga memiliki versi sendiri
realloc
dancalloc
dan seterusnya serta struktur data C ++ yang sesuai standar yang dibangun pada konsep yang sama, tetapi saya mengekstrak minimum yang diperlukan untuk menggambarkan konsep tersebut.Memang ada peringatan bahwa itu berbahaya untuk disalin dan Anda tidak boleh mengembalikan pointer yang dialokasikan melaluinya (mereka bisa berakhir tidak valid karena
FastMem
instance dihancurkan). Ini dimaksudkan untuk digunakan untuk kasus-kasus sederhana dalam lingkup fungsi lokal di mana Anda akan tergoda untuk selalu menggunakan stack / VLA sebaliknya jika beberapa kasus yang jarang terjadi dapat menyebabkan buffer / stack overflow. Ini bukan pengalokasi tujuan umum dan tidak boleh digunakan seperti itu.Saya benar-benar menciptakannya beberapa waktu lalu sebagai tanggapan terhadap situasi dalam basis kode lama menggunakan C89 bahwa mantan tim berpikir tidak akan pernah terjadi di mana pengguna berhasil menamai item dengan nama yang lebih dari 2.047 karakter (mungkin dia tertidur di keyboard-nya) ). Rekan-rekan saya benar-benar mencoba untuk meningkatkan ukuran array yang dialokasikan di berbagai tempat menjadi 16.384 sebagai respons di mana saya pikir itu semakin konyol dan hanya menukar risiko stack overflow yang lebih besar dengan imbalan risiko buffer overflow yang lebih rendah. Ini memberikan solusi yang sangat mudah dipasang untuk memperbaiki kasus-kasus itu dengan hanya menambahkan beberapa baris kode. Hal ini memungkinkan kasus umum ditangani dengan sangat efisien dan masih menggunakan tumpukan tanpa kasus-kasus langka gila yang menuntut tumpukan perangkat lunak. Namun, saya telah menemukannya bermanfaat sejak saat itu bahkan setelah C99 karena VLA masih tidak dapat melindungi kita terhadap tumpukan berlebihan. Yang ini bisa tetapi masih menyatu dari tumpukan untuk permintaan alokasi kecil.
sumber
The Stack panggilan selalu terbatas. Pada sistem operasi utama seperti Linux atau Windows, batasnya adalah satu atau beberapa megabyte (dan Anda bisa menemukan cara untuk mengubahnya). Dengan beberapa aplikasi multi-utas, ini bisa lebih rendah (karena utas dapat dibuat dengan tumpukan yang lebih kecil). Pada sistem embedded, ukurannya bisa sekecil beberapa kilobyte. Aturan praktis yang baik adalah untuk menghindari frame panggilan yang lebih besar dari beberapa kilobyte.
Jadi menggunakan VLA masuk akal hanya jika Anda yakin bahwa Anda
len
cukup kecil (paling banyak beberapa puluh ribu). Kalau tidak, Anda memiliki tumpukan overflow dan itu adalah kasus perilaku yang tidak terdefinisi , situasi yang sangat menakutkan .Namun, dengan menggunakan panduan alokasi memori C dinamis (misalnya
calloc
ataumalloc
&free
) juga memiliki kekurangan:itu bisa gagal dan Anda harus selalu menguji kegagalan (misalnya
calloc
ataumalloc
kembaliNULL
).itu lebih lambat: alokasi VLA yang sukses membutuhkan beberapa nanodetik, yang berhasil
malloc
bisa memerlukan beberapa mikrodetik (dalam kasus yang baik, hanya sebagian kecil dari mikrodetik) atau bahkan lebih (dalam kasus patologis yang melibatkan meronta - ronta , lebih banyak lagi).jauh lebih sulit untuk dikodekan: Anda
free
hanya bisa ketika Anda yakin bahwa zona runcing tidak lagi digunakan. Dalam kasus Anda, Anda dapat menghubungi keduanyacalloc
danfree
dalam rutinitas yang sama.Jika Anda tahu bahwa sebagian besar waktu Anda
result
(nama yang sangat buruk, Anda tidak boleh mengembalikan alamat variabel VLA otomatis ; jadi saya menggunakanbuf
alih-alih diresult
bawah) kecil, Anda dapat menggunakan huruf besar, misalnyaNamun, kode di atas kurang dapat dibaca dan mungkin optimasi prematur. Namun itu lebih kuat daripada solusi VLA murni.
PS. Beberapa sistem (misalnya beberapa distribusi Linux diaktifkan secara default) memiliki overcommitment memori (yang membuat
malloc
memberikan beberapa pointer bahkan jika tidak ada cukup memori). Ini adalah fitur yang saya sukai dan biasanya dinonaktifkan pada mesin Linux saya.sumber