Bagaimana cara menghapus [] “mengetahui” ukuran array operan?

250
Foo* set = new Foo[100];
// ...
delete [] set;

Anda tidak meneruskan batasan array ke delete[]. Tetapi di mana informasi itu disimpan? Apakah standar?

VolkerK
sumber
sourceforge.net/projects/fastmm adalah open source dan menggantikan memory-manager. Di sini Anda dapat mengetahui cara kerja manajemen memori dan dari mana informasi berasal untuk mengalokasikan dan menghapus memori.
1
Perhatikan bahwa FastMM khusus untuk kompiler Delphi / C ++ Builder saja, itu bukan manajer memori untuk C ++. Bahkan tidak ditulis dalam C ++.
Remy Lebeau

Jawaban:

181

Ketika Anda mengalokasikan memori pada heap, pengalokasi Anda akan melacak berapa banyak memori yang telah Anda alokasikan. Ini biasanya disimpan dalam segmen "head" tepat sebelum memori yang Anda peroleh dialokasikan. Dengan begitu ketika saatnya membebaskan memori, de-alokasi tahu persis berapa banyak memori yang harus dibebaskan.

Peter Kühne
sumber
4
Perhatikan bahwa ini hanya berlaku untuk alokasi array di C ++. Semua alokasi lainnya bergantung pada ukuran tipe. Beberapa perpustakaan menyimpan semua ukuran alokasi, biasanya hanya dalam mode debugging.
Zan Lynx
97
Sama sekali tidak ada alasan mengapa informasi ini tidak tersedia untuk programmer. Saya dapat melewatkan pointer ke suatu fungsi dan membebaskannya, tetapi untuk mendapatkan ukurannya sendiri dalam fungsi yang sama saya harus memberikan parameter tambahan. Apakah itu masuk akal?
Mark Ruzon
26
@ Mark, itu membuat sejumlah kecil akal, karena secara teori itu membebaskan pengalokasi untuk selalu menyimpan ukuran blok yang dialokasikan (yang mungkin berbeda dari ukuran blok yang diminta ). Desain pengalokasi tertentu mungkin memerlukan informasi ini untuk tujuan mereka sendiri, atau mungkin tidak cukup canggih untuk menggunakan informasi jenis untuk melacak ukuran alokasi tumpukan non-array, dll. Memaksa pengalokasi untuk menyimpan ukuran yang diminta (sehingga Anda tidak akan perlu untuk melewati ukuran array sendiri) mungkin merupakan beban kecil, tetapi bisa memiliki dampak kinerja pada desain pengalokasi yang mungkin.
Doug McClean
33
Maaf, tapi jawaban ini tidak tepat. Apa yang dijelaskan QuantumPete pada dasarnya adalah "Bagaimana freemengetahui berapa banyak memori yang harus dialokasikan". Ya, ukuran blok memori disimpan "di suatu tempat" oleh malloc(biasanya di blok itu sendiri), jadi begitulah caranya free. Namun, new[]/ delete[]adalah cerita yang berbeda. Yang terakhir pada dasarnya bekerja di atas malloc/ free. new[]juga menyimpan sejumlah elemen yang dibuatnya dalam blok memori (terlepas dari malloc), sehingga nantinya delete[]dapat mengambil dan menggunakan nomor itu untuk memanggil jumlah destruktor yang tepat.
AnT
26
Yaitu secara fisik dua penghitung disimpan di blok: ukuran blok (berdasarkan malloc) dan jumlah elemen (berdasarkan new[]). Perhatikan, bahwa yang pertama tidak dapat digunakan untuk menghitung yang terakhir, karena secara umum ukuran blok memori bisa lebih besar dari yang sebenarnya diperlukan untuk array ukuran yang diminta. Perhatikan juga bahwa penghitung elemen array hanya diperlukan untuk tipe dengan destruktor non-sepele. Untuk jenis dengan destruktor sepele penghitung tidak disimpan oleh new[]dan, tentu saja, tidak diambil oleh delete[].
AnT
23

SALAH SATU pendekatan untuk kompiler adalah mengalokasikan sedikit lebih banyak memori dan untuk menyimpan sejumlah elemen dalam elemen head.

Contoh bagaimana itu bisa dilakukan:

Sini

int* i = new int[4];

kompiler akan mengalokasikan sizeof(int)*5byte.

int *temp = malloc(sizeof(int)*5)

Akan menyimpan "4" dalam sizeof(int)byte pertama

*temp = 4;

dan mengatur i

i = temp + 1;

Jadi iakan menunjuk ke array 4 elemen, bukan 5.

Dan penghapusan

delete[] i;

akan diproses dengan cara berikut:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Avt
sumber
9

Informasi tidak terstandarisasi. Namun dalam platform yang saya kerjakan pada informasi ini disimpan dalam memori tepat sebelum elemen pertama. Karena itu Anda secara teoritis dapat mengaksesnya dan memeriksanya, namun itu tidak sepadan.

Ini juga mengapa Anda harus menggunakan delete [] ketika Anda mengalokasikan memori dengan baru [], karena versi array dari delete tahu bahwa (dan di mana) ia perlu mencari untuk membebaskan jumlah memori yang tepat - dan memanggil jumlah destruktor yang sesuai. untuk benda-benda.

Daemin
sumber
5

Pada dasarnya ini diatur dalam memori sebagai:

[info] [Anda minta ...]

Di mana info adalah struktur yang digunakan oleh kompiler Anda untuk menyimpan jumlah memori yang dialokasikan, dan apa yang tidak.

Ini tergantung implementasi meskipun.

Francisco Soto
sumber
4

Ini bukan sesuatu yang ada dalam spesifikasi - tergantung pada implementasi.

jeffm
sumber
3

Ini didefinisikan dalam standar C ++ untuk menjadi kompiler spesifik. Yang berarti sihir kompiler. Ini dapat memutuskan dengan pembatasan penyelarasan non-sepele pada setidaknya satu platform utama.

Anda bisa memikirkan kemungkinan implementasi dengan menyadari bahwa delete[]hanya ditentukan untuk pointer yang dikembalikan oleh new[], yang mungkin bukan pointer yang sama seperti yang dikembalikan oleh operator new[]. Salah satu implementasi di wild adalah untuk menyimpan jumlah array di int pertama yang dikembalikan oleh operator new[], dan telah new[]mengembalikan pointer offset di masa lalu. (Inilah sebabnya mengapa keberpihakan non-sepele bisa pecah new[].)

Ingatlah itu operator new[]/operator delete[]! = new[]/delete[].

Plus, ini ortogonal untuk bagaimana C mengetahui ukuran memori yang dialokasikan oleh malloc.

MSN
sumber
2

Karena array yang akan 'dihapus' seharusnya dibuat dengan menggunakan satu operator 'baru'. Operasi 'baru' seharusnya meletakkan informasi itu di tumpukan. Kalau tidak, bagaimana cara tambahan menggunakan baru tahu di mana tumpukan berakhir?

Joel Coehoorn
sumber
0

Itu tidak standar. Dalam runtime Microsoft, operator baru menggunakan malloc () dan operator hapus menggunakan gratis (). Jadi, dalam pengaturan ini pertanyaan Anda setara dengan yang berikut: Bagaimana cara bebas () mengetahui ukuran blok?

Ada beberapa pembukuan yang terjadi di belakang layar, yaitu di runtime C.

Andre
sumber
5
Tidak benar. Sebelum menelepon gratis, delete [] harus memanggil destruktor terlebih dahulu. Untuk mengetahui ukuran alokasi total ini tidak cukup. Sebenarnya [] dan hapus [] yang baru berfungsi berbeda untuk tipe polos dan yang dihancurkan di VC ++.
Suma
0

Ini adalah masalah yang lebih menarik daripada yang mungkin Anda pikirkan pada awalnya. Balasan ini adalah tentang satu kemungkinan implementasi.

Pertama, ketika pada tingkat tertentu sistem Anda harus tahu bagaimana 'membebaskan' blok memori, malloc yang mendasari / gratis (yang biasanya dipanggil / hapus / baru [] / hapus [] umumnya) tidak selalu ingat persis berapa banyak memori Anda meminta, itu bisa dibulatkan ke atas (misalnya, setelah Anda berada di atas 4K sering dibulatkan ke blok berukuran 4K berikutnya).

Oleh karena itu, bahkan jika bisa mendapatkan ukuran blok memori, itu tidak memberi tahu kami berapa banyak nilai dalam memori ed [] baru, karena bisa lebih kecil. Oleh karena itu, kita harus menyimpan integer tambahan yang memberitahu kita berapa banyak nilai yang ada.

KECUALI, jika tipe yang sedang dibangun tidak memiliki destruktor, maka hapus [] tidak harus melakukan apa pun kecuali membebaskan blok memori, dan karenanya tidak perlu menyimpan apa pun!

Chris Jefferson
sumber