Mengapa alokasi () tidak dianggap praktik yang baik?

alloca() mengalokasikan memori pada stack, bukan pada heap, seperti dalam kasus malloc() . Karena itu, ketika saya kembali dari rutinitas, memori dilepaskan. Jadi ini sebenarnya memecahkan masalah saya melepaskan memori yang dialokasikan secara dinamis. Membebaskan memori yang dialokasikan melalui malloc() adalah sakit kepala yang besar dan, jika Anda melewatkannya, menyebabkan semua jenis masalah memori.

Mengapa penggunaan alloca() direkomendasikan meskipun fungsi di atas?

341
19 июня '09 в 19:24 2009-06-19 19:24 Vaibhav diatur pada 19 Juni 2009 pada 19:24 2009-06-19 19:24
@ 25 jawaban

Jawabannya tepat di halaman man (setidaknya di Linux ):

RETURN VALUE Fungsi Alokasi () mengembalikan pointer ke awal ruang yang dialokasikan. Jika distribusi menyebabkan stack overflow, perilaku program tidak ditentukan.

Itu tidak berarti bahwa itu tidak boleh digunakan. Salah satu proyek OSS yang sedang saya kerjakan adalah menggunakannya secara luas, dan jika Anda tidak menyalahgunakannya ( alloca nilai yang sangat besar), itu tidak masalah. Ketika Anda melewati tanda "beberapa ratus byte", saatnya untuk menggunakan malloc dan teman-teman. Anda masih bisa mendapatkan kesalahan alokasi, tetapi setidaknya Anda akan memiliki semacam indikasi kesalahan, bukan hanya tumpukan tumpukan.

200
19 июня '09 в 19:27 2009-06-19 19:27 jawabannya diberikan oleh Sean Bright 19 Juni '09 pada 19:27 2009-06-19 19:27

Salah satu kesalahan paling berkesan yang saya miliki adalah melakukannya dengan fungsi alloca menggunakan alloca . Itu memanifestasikan dirinya sebagai stack overflow (karena dialokasikan pada stack) pada titik-titik acak dalam program.

Dalam file header:

 void DoSomething() { wchar_t* pStr = alloca(100); //...... } 

Dalam file implementasi:

 void Process() { for (i = 0; i < 1000000; i++) { DoSomething(); } } 

Jadi, apa yang terjadi adalah fungsi built-in dari kompiler DoSomething , dan semua alokasi stack dilakukan di dalam fungsi Process() dan, dengan demikian, meniup stack. Dalam pembelaan saya (dan saya bukan orang yang menemukan pertanyaan ini, saya harus pergi dan berteriak kepada salah satu pengembang senior ketika saya tidak bisa memperbaikinya), itu bukan alloca >

Jadi pelajarannya adalah - jangan gunakan alloca dalam fungsi yang Anda pikir dapat tertanam.

177
05 авг. jawabannya diberikan oleh Igor Zevaka 05 Agustus. 2010-08-05 02:35 '10 pada pukul 02:35 2010-08-05 02:35

Sebuah pertanyaan lama, tetapi tidak ada yang menyebutkan bahwa itu harus diganti dengan array dengan panjang variabel.

 char arr[size]; 

bukannya

 char *arr=alloca(size); 

Itu dalam standar C99 dan ada sebagai ekstensi kompiler di banyak kompiler.

63
01 авг. Jawabannya diberikan oleh Patrick Schlüter 01 Agustus. 2010-08-01 23:45 '10 pada 23:45 2010-08-01 23:45

Alokasi () sangat berguna jika Anda tidak dapat menggunakan variabel lokal standar, karena ukurannya harus ditentukan saat runtime, dan Anda benar - benar dapat menjamin bahwa pointer yang Anda dapatkan dari Alokasi () tidak akan pernah digunakan setelah fungsi ini kembali .

Anda bisa cukup aman jika Anda

  • jangan mengembalikan pointer atau apapun yang mengandungnya.
  • jangan menyimpan pointer dalam struktur apa pun yang dipilih pada heap
  • jangan izinkan orang lain menggunakan pointer

Bahaya nyata datang dari kemungkinan bahwa orang lain akan me>

52
23 июня '09 в 2:46 2009-06-23 02:46 jawabannya diberikan oleh Arthur Ulfeldt 23 Juni '09 pada 2:46 2009-06-23 02:46

Sebagaimana dicatat dalam publikasi ini di newsgroup , ada beberapa alasan mengapa penggunaan alloca dapat dianggap sulit dan berbahaya:

  • Tidak semua kompiler mendukung alloca .
  • Beberapa kompiler menafsirkan perilaku yang diinginkan dari alloca berbeda, sehingga portabilitas tidak dijamin bahkan di antara kompiler yang mendukungnya.
  • Beberapa implementasi salah.
37
19 июня '09 в 19:28 2009-06-19 19:28 jawabannya diberikan oleh FreeMemory 19 Juni 09 di 19:28 2009-06-19 19:28

Satu masalah adalah bahwa itu bukan standar, meskipun didukung secara luas. Hal-hal lain dianggap sama, saya akan selalu menggunakan fungsi standar, bukan ekstensi umum dari kompiler.

25
19 июня '09 в 19:35 2009-06-19 19:35 Jawaban diberikan oleh David Thornley 19 Juni 2009 di 19:35 2009-06-19 19:35

masih mengalokasikan penggunaan tidak dianjurkan, mengapa?

Saya tidak melihat konsensus seperti itu. Banyak pro kuat; beberapa kontra:

  • C99 menyediakan array panjang variabel, yang sering digunakan secara istimewa, karena notasi lebih konsisten dengan array panjang tetap dan umum intuitif
  • banyak sistem memiliki jumlah memori / ruang alamat untuk stack paling sedikit daripada tumpukan, yang membuat program sedikit lebih rentan terhadap kelelahan memori (melalui stack overflow): ini dapat dianggap sebagai hal yang baik atau buruk - salah satu alasan mengapa stack tidak otomatis tumbuh seperti yang dilakukan sekelompok orang untuk mencegah penggunaan program kontrol yang tidak terkendali di seluruh mesin.
  • ketika digunakan di area yang lebih lokal (misalnya, dalam beberapa while atau for while ) atau di beberapa area memori, memori diakumulasikan untuk setiap iterasi / ruang lingkup dan tidak dirilis sampai fungsi dilepaskan: ini kontras dengan variabel tertentu dalam struktur kontrol (misalnya, for {int i = 0; i < 2; ++i) { X } akan mengakumulasikan memori yang alloca diminta dalam X, tetapi memori untuk array ukuran tetap akan diproses menjadi iterasi).
  • Kompiler modern biasanya tidak menjalankan fungsi inline yang memanggil alloca , tetapi jika Anda memaksakannya, alloca akan muncul dalam konteks pemanggil (mis., Tumpukan tidak akan dilepaskan sampai pemanggil kembali)
  • jauh sebelumnya, alloca dari fungsi yang tidak tertahankan / meretas ke ekstensi standar, tetapi beberapa persepsi negatif mungkin bertahan
  • seumur hidup terikat pada lingkup fungsi yang mungkin cocok atau tidak sesuai dengan programmer lebih baik daripada kontrol eksplisit malloc
  • untuk menggunakan malloc , itu mendorong berpikir tentang melepaskan - jika itu dikendalikan melalui fungsi pembungkus (misalnya, WonderfulObject_DestructorFree(ptr) ), maka fungsi menyediakan titik untuk operasi pembersihan (misalnya, menangani pegangan file, melepaskan pointer internal, atau menjalankan beberapa protokol) tanpa perubahan eksplisit. kode klien: kadang-kadang model yang bagus untuk adopsi yang konsisten
    • dalam pemrograman gaya pseudo-OO ini, tentu saja, Anda menginginkan sesuatu seperti WonderfulObject* p = WonderfulObject_AllocConstructor(); - ini dimungkinkan ketika "konstruktor" adalah fungsi yang mengembalikan memori ke malloc -ed (karena memori tetap dialokasikan setelah fungsi mengembalikan nilai yang harus disimpan dalam p ), tetapi tidak jika "konstruktor" menggunakan alloca
      • Versi makro dari WonderfulObject_AllocConstructor dapat mencapai ini, tetapi "macro are evil" karena mereka dapat saling bertentangan dan non-macrocode dan membuat pergantian yang tidak diinginkan dan karenanya sulit untuk mendiagnosis masalah.
    • Operasi free hi>alloca() (misalnya, GCC) menggunakan makro builtin untuk alloca() , jadi mengganti perpustakaan memori untuk digunakan saat runtime adalah mustahil, seperti yang dilakukan untuk malloc / realloc / free (misalnya, pagar listrik).
  • Beberapa implementasi memiliki masalah halus: misalnya, dari halaman manual Linux:

    Dalam banyak sistem, pengalokasian () tidak dapat digunakan di dalam daftar argumen pemanggilan fungsi, karena ruang stack yang disediakan oleh pengalokasian () akan ditampilkan dalam tumpukan di tengah ruang untuk argumen fungsi.


Saya tahu bahwa pertanyaan ini ditandai C, tetapi sebagai programmer C ++, saya pikir saya akan menggunakan C ++ untuk mengilustrasikan utilitas alloca potensial: kode di bawah ini (dan di sini ideone ) menciptakan pelacakan vektor jenis polimorfik dengan berbagai ukuran yang dialokasikan oleh stack (dengan mengacu pada masa pakai untuk mengembalikan fungsi), dan bukan tumpukan khusus.

 #include <alloca.h> #include <iostream> #include <vector> struct Base { virtual ~Base() { } virtual int to_int() const = 0; }; struct Integer : Base { Integer(int n) : n_(n) { } int to_int() const { return n_; } int n_; }; struct Double : Base { Double(double n) : n_(n) { } int to_int() const { return -n_; } double n_; }; inline Base* factory(double d) __attribute__((always_inline)); inline Base* factory(double d) { if ((double)(int)d != d) return new (alloca(sizeof(Double))) Double(d); else return new (alloca(sizeof(Integer))) Integer(d); } int main() { std::vector<Base*> numbers; numbers.push_back(factory(29.3)); numbers.push_back(factory(29)); numbers.push_back(factory(7.1)); numbers.push_back(factory(2)); numbers.push_back(factory(231.0)); for (std::vector<Base*>::const_iterator i = numbers.begin(); i != numbers.end(); ++i) { std::cout << *i << ' ' << (*i)->to_int() << '\n'; (*i)->~Base(); // optionally / else Undefined Behaviour iff the // program depends on side effects of destructor } } 
21
26 февр. jawabannya diberikan oleh Tony Delroy pada 26 Februari. 2013-02-26 13:31 '13 pada 13:31 2013-02-26 13:31

Semua jawaban lain benar. Namun, jika hal yang ingin Anda alokasikan dengan alloca() cukup kecil, saya pikir ini adalah teknik yang baik yang lebih cepat dan lebih mudah daripada menggunakan malloc() atau sebaliknya.

Dengan kata lain, alloca( 0x00ffffff ) berbahaya dan dapat menyebabkan meluap, sama seperti char hugeArray[ 0x00ffffff ]; . Berhati-hatilah dan masuk akal, dan semuanya akan baik-baik saja.

11
19 июня '09 в 19:32 2009-06-19 19:32 jawabannya diberikan oleh JSB 19 19 Juni, '09 pada 19:32 2009-06-19 19:32

Semua orang sudah menunjukkan hal besar, yang merupakan perilaku potensial yang tidak ditentukan, tetapi saya harus menyebutkan bahwa di lingkungan Windows ada mekanisme besar untuk menangkapnya menggunakan pengecualian terstruktur (SEH) dan halaman keamanan. Karena tumpukan hanya tumbuh sesuai kebutuhan, halaman keamanan ini berada di area yang tidak dialokasikan. Jika Anda memilih di dalamnya (oleh), pengecualian dihasilkan.

Anda dapat menangkap pengecualian SEH ini dan memanggil _resetstkoflw untuk mengatur u>

Saya sarankan membatasi ukuran maksimum seleksi dengan membungkus alokasi dan melacaknya secara internal. Jika Anda sangat hardcore, Anda bisa melempar tontonan di bagian atas fungsi Anda untuk melacak alokasi alokasi dalam hal fungsi dan kewarasan, mengujinya untuk jumlah maksimum yang diizinkan untuk proyek Anda.

Selain itu, selain mencegah kebocoran memori, alokasi tidak menyebabkan fragmentasi memori, yang sangat penting. Saya tidak berpikir bahwa alokasi adalah praktik yang buruk jika Anda menggunakannya dengan bijak, yang pada dasarnya berlaku untuk semuanya: -)

11
21 марта '11 в 19:19 2011-03-21 19:19 jawabannya diberikan kepada SilentDirge pada 21 Maret '11 pada 19:19 2011-03-21 19:19

Ada banyak jawaban menarik untuk pertanyaan "lama" ini, bahkan beberapa jawaban yang relatif baru, tetapi saya belum menemukan penyebutan ini.

Ketika digunakan dengan benar dan dengan hati-hati, menggunakan alloca() secara berurutan (mungkin untuk seluruh aplikasi) untuk menangani distribusi panjang variabel kecil (atau C99 VLA, jika tersedia) dapat menghasilkan jumlah pertumbuhan yang lebih rendah daripada implementasi yang setara menggunakan array lokal dengan panjang tetap yang terlalu besar. Karenanya, alloca() bisa baik untuk tumpukan Anda jika Anda menggunakannya dengan hati-hati.

Saya menemukan kutipan ini .... Baiklah, saya membuat kutipan ini. Tapi sungguh, pikirkan tentang itu ....

@j_random_hacker sangat benar dalam komentarnya pada jawaban lain: menghindari alloca() mendukung array lokal besar tidak membuat program Anda lebih aman dari (jika kompiler Anda belum cukup tua untuk memungkinkan penyematan fungsi yang menggunakan alloca() , dalam hal ini Dalam hal ini, Anda harus memperbarui atau jika Anda tidak menggunakan siklus alloca() internal alloca() , dalam hal ini Anda tidak boleh menggunakan siklus internal alloca() .

Saya bekerja di lingkungan desktop / server dan sistem embedded. Banyak sistem tertanam tidak menggunakan banyak sama sekali (mereka bahkan tidak merujuk pada dukungan) karena alasan yang mencakup persepsi bahwa memori yang didistribusikan secara dinamis adalah jahat karena risiko kebocoran memori dalam aplikasi yang tidak pernah di-boot u>

alloca() (atau VLA) dapat menjadi alat yang tepat untuk pekerjaan itu.

Saya melihat lagi dan lagi di mana programmer membuat buffer, didistribusikan di seluruh tumpukan, "cukup besar untuk menangani setiap kasus yang mungkin." Di pohon panggilan yang sangat bersarang, menggunakan kembali templat ini (anti?) Menyebabkan penggunaan stack yang berlebihan. (Bayangkan bahwa pohon panggilan sedalam 20 level, di mana pada setiap level, karena berbagai alasan, fungsinya secara membabi buta mendistribusikan buffer 1024-byte hanya untuk keamanan, ketika biasanya hanya akan menggunakan 16 atau kurang dari itu, dan hanya kasus yang sangat jarang dapat menggunakan lebih banyak.) Alternatifnya adalah menggunakan alloca() atau VLA dan hanya mengalokasikan ruang stack sebanyak yang dibutuhkan fungsi Anda untuk menghindari beban yang tidak perlu pada tumpukan. Kami berharap bahwa ketika satu fungsi di pohon panggilan perlu mendistribusikan lebih dari biasanya, yang lain di pohon panggilan masih menggunakan distribusi kecil yang biasa, dan keseluruhan penggunaan tumpukan aplikasi jauh lebih kecil daripada jika masing-masing fungsi secara membabi buta mengalokasikan buffer lokal.

Tetapi jika Anda memutuskan untuk menggunakan ...

Berdasarkan jawaban lain pada halaman ini, tampaknya VLA harus aman (mereka tidak menggabungkan alokasi tumpukan jika mereka dipanggil dari loop), tetapi jika Anda menggunakan alloca() , berhati-hatilah untuk tidak menggunakannya di dalam loop dan pastikan Anda Suatu fungsi tidak dapat digarisbawahi jika ada kemungkinan ia dapat dipanggil dalam siklus fungsi lainnya.

9
01 апр. jawabannya diberikan oleh phonetagger 01 April. 2016-04-01 01:00 '16 pada 1:00 2016-04-01 01:00

Itu sebabnya:

 char x; char *y=malloc(1); char *z=alloca( *z = 1; 

Bukan berarti siapa pun yang menulis kode ini, tetapi argumen ukuran yang Anda alloca untuk hampir pasti berasal dari beberapa masukan yang dapat membuat program Anda alloca sesuatu yang besar. Lagi pula, jika ukurannya tidak berbasis input atau tidak memiliki kapasitas untuk menjadi besar, mengapa Anda tidak mendeklarasikan buffer lokal kecil dengan ukuran tetap?

Hampir semua kode yang menggunakan alloca dan / atau C99 vlas memiliki kesalahan serius yang akan menyebabkan kegagalan (jika Anda beruntung) atau untuk kompromi hak istimewa (jika Anda tidak beruntung).

9
01 авг. Jawabannya adalah R .. 01 Agustus 2010-08-01 23:32 '10 pada 23:32 2010-08-01 23:32

mengalokasikan () baik dan efisien ... tetapi juga rusak parah.

  • perilaku dengan lingkup yang dimodifikasi (lingkup alih-alih ruang lingkup blok)
  • gunakan tidak konsisten dengan malloc ( alokasia); pointer yang ditentukan tidak boleh dibebaskan; untuk selanjutnya, Anda harus melacak di mana pointer pergi dari yang gratis () yang Anda miliki dengan malloc () )
  • perilaku buruk ketika Anda juga menggunakan inlining (ruang lingkup kadang-kadang transisi ke fungsi pemanggil, tergantung pada apakah permintaan dikonfigurasi atau tidak).
  • cek batas stack
  • perilaku tidak terdefinisi dalam hal kegagalan (tidak mengembalikan NULL, seperti malloc ... dan itu berarti kegagalan, karena tidak memeriksa batas tumpukan dalam kasus apa pun ...)
  • bukan standar ansi

Dalam kebanyakan kasus, Anda dapat menggantinya menggunakan variabel lokal dan ukuran majorant. Jika digunakan untuk objek besar, menempatkannya di tumpukan biasanya merupakan ide yang lebih aman.

Jika Anda benar-benar membutuhkannya, Anda dapat menggunakan VLA (tidak ada vla di C ++, terlalu buruk). Mereka jauh lebih baik daripada pengalokasian () tentang perilaku dan konsistensi daerah. Seperti yang saya lihat, VLA adalah semacam alokasi () .

Tentu saja, struktur lokal atau array menggunakan majorant dari ruang yang dibutuhkan masih lebih baik, dan jika Anda tidak memiliki distribusi heap majorant menggunakan malloc sederhana (), mungkin masuk akal. Saya tidak melihat kasus penggunaan yang masuk akal ketika Anda benar-benar membutuhkan alokasi () atau VLA.

9
02 сент. jawabannya diberikan Kriss 02 September . 2014-09-02 13:26 '14 pada 13:26 2014-09-02 13:26

Tempat di mana alloca() lebih berbahaya daripada malloc() - kernel - inti dari sistem operasi khas memiliki ruang stack tetap, dikodekan ke dalam salah satu header-nya; itu tidak sefleksibel tumpukan aplikasi. Melakukan panggilan alloca() dengan ukuran yang tidak dapat dibenarkan dapat menyebabkan crash kernel. Beberapa kompiler memperingatkan tentang penggunaan alloca() (dan bahkan VLA, dalam hal ini) dengan parameter tertentu yang harus disertakan ketika mengkompilasi kode kernel. Di sini lebih baik untuk mengalokasikan memori dalam tumpukan yang tidak diperbaiki oleh konfigurasi yang kaku, batas kode.

7
27 нояб. jawaban yang diberikan oleh Sondhi Chakraborty 27 November 2010-11-27 21:49 '10 pada jam 21:49 2010-11-27 21:49

Sayangnya, alloca() benar-benar menakjubkan alloca() hi>alloca() memiliki alloca() .

  • Dia menabur benih kehancurannya sendiri. Dengan pengembalian sebagai destruktor.

  • Seperti malloc() , ia mengembalikan pointer yang tidak valid menjadi gagal, yang akan menjadi segfault pada sistem modern dengan MMU (dan, saya harap, restart tanpa).

  • Tidak seperti variabel otomatis, Anda dapat menentukan ukuran pada waktu berjalan.

Ini bekerja dengan baik dengan rekursi. Anda dapat menggunakan variabel statis untuk mencapai sesuatu seperti rekursi ekor dan hanya menggunakan beberapa yang lain untuk meneruskan informasi ke setiap iterasi.

Jika Anda menekan terlalu dalam, Anda yakin akan segfault (jika Anda memiliki MMU).

Perhatikan bahwa malloc() tidak menawarkan lagi, karena ia mengembalikan NULL (yang juga akan menjadi segfault jika ditugaskan) ketika sistem mati. Artinya, yang bisa Anda lakukan hanyalah janji atau coba tunjuk saja.

Untuk menggunakan malloc() , saya menggunakan variabel global dan menetapkannya ke NULL. Если указатель не равен NULL, я освобожу его, прежде чем использовать malloc() .

Вы также можете использовать realloc() в качестве общего случая, если хотите скопировать любые существующие данные. Перед тем, как работать, вам нужно проверить указатель, если вы собираетесь копировать или конкатенировать после realloc() .

3.2.5.2 Преимущества alloca

4
ответ дан zagam 30 марта '11 в 9:58 2011-03-30 09:58