C ++ new int [0] - apakah ini akan mengalokasikan memori?

241

Aplikasi tes sederhana:

cout << new int[0] << endl;

output:

0x876c0b8

Jadi sepertinya itu berfungsi. Apa yang dikatakan standar tentang ini? Apakah selalu legal untuk "mengalokasikan" blok memori kosong?


sumber
4
+1 Pertanyaan yang sangat menarik - walaupun saya tidak yakin seberapa penting kode sebenarnya.
Zifre
37
@ Zifre: Saya meminta rasa ingin tahu, tetapi mungkin penting di dunia nyata, mis. Ketika ukuran blok memori yang dialokasikan dihitung dalam beberapa cara, dan hasil perhitungannya mungkin nol, maka tidak ada kebutuhan langsung untuk menambahkan pengecualian untuk tidak mengalokasikan blok berukuran nol .. Karena mereka harus dialokasikan dan dihapus tanpa kesalahan (jika hanya blok berukuran nol tidak direferensikan). Jadi umumnya ini memberikan abstraksi yang lebih luas tentang apa itu blok memori.
2
@ emg-2: Dalam situasi contoh Anda, sebenarnya tidak masalah, karena delete [] sah-sah saja pada pointer NULL :-).
Evan Teran
2
Ini hanya terkait secara tangensial - jadi saya berkomentar di sini - tetapi C ++ dalam banyak hal memastikan bahwa objek yang berbeda memiliki alamat unik ... bahkan jika mereka tidak secara eksplisit memerlukan penyimpanan. Eksperimen terkait akan memeriksa ukuran struct kosong. Atau sebuah array dari struct itu.
Drew Dormann
2
Untuk menguraikan komentar Shmoopty: Terutama ketika pemrograman dengan templat (misalnya templat kelas kebijakan seperti std :: pengalokasi), adalah umum di C ++ untuk memiliki objek berukuran nol. Kode generik mungkin perlu secara dinamis mengalokasikan objek tersebut dan menggunakan pointer ke mereka untuk membandingkan identitas objek. Inilah sebabnya mengapa operator baru () mengembalikan pointer unik untuk permintaan berukuran nol. Meskipun bisa dibilang kurang penting / umum, alasan yang sama berlaku untuk alokasi array dan operator baru [] ().
Trevor Robinson

Jawaban:

233

Dari 5.3.4 / 7

Ketika nilai ekspresi dalam deklarator langsung-baru adalah nol, fungsi alokasi dipanggil untuk mengalokasikan array tanpa elemen.

Dari 3.7.3.1/2

Efek dereferencing pointer dikembalikan sebagai permintaan untuk ukuran nol tidak ditentukan.

Juga

Bahkan jika ukuran ruang yang diminta [oleh baru] adalah nol, permintaan tersebut dapat gagal.

Itu berarti Anda bisa melakukannya, tetapi Anda tidak bisa secara legal (dengan cara yang didefinisikan dengan baik di semua platform) dereferensi memori yang Anda dapatkan - Anda hanya bisa meneruskannya ke array delete - dan Anda harus menghapusnya.

Berikut ini adalah catatan kaki yang menarik (yaitu bukan bagian normatif dari standar, tetapi termasuk untuk tujuan ekspositori) yang dilampirkan pada kalimat dari 3.7.3.1/2

[32] Tujuannya adalah agar operator baru () dapat diterapkan dengan memanggil malloc () atau calloc (), sehingga aturannya pada dasarnya sama. C ++ berbeda dari C yang membutuhkan nol permintaan untuk mengembalikan pointer non-null.]

Faisal Vali
sumber
Saya mendapatkan kebocoran memori jika saya tidak menghapus. Apakah itu diharapkan? Setidaknya saya tidak berharap.
EralpB
12
@EralpB: sebaliknya, bahkan jika permintaan Anda nol, alokasi ini terjadi di Heap, di mana satu permintaan menyiratkan beberapa operasi pembukuan, seperti mengalokasikan dan menginisialisasi penjaga tumpukan sebelum dan setelah zona yang diberikan oleh pengalokasi, penyisipan ke daftar bebas atau struktur mengerikan kompleks lainnya. Membebaskannya berarti melakukan pembukuan yang terbelakang.
v.oddou
3
@RalpB ya saya kira Anda dapat mengharapkan kebocoran memori setiap kali Anda tidak menyeimbangkan new[]dengan delete[]- apa pun ukurannya. Khususnya, ketika Anda menelepon, new[i]Anda membutuhkan sedikit lebih banyak memori daripada yang Anda coba alokasikan untuk menyimpan ukuran array (yang nantinya digunakan oleh delete[]saat deallocating)
pqnet
24

Ya, itu legal untuk mengalokasikan array berukuran nol seperti ini. Tetapi Anda juga harus menghapusnya.


sumber
1
Apakah Anda memiliki kutipan untuk ini? Kita semua tahu itu int ar[0];ilegal mengapa baru OK?
Motti
4
Sangat menarik bahwa C ++ tidak begitu kuat dengan melarang benda berukuran nol. Pikirkan optimasi kelas dasar kosong: Di sini juga, sub-objek kelas dasar kosong mungkin memiliki ukuran nol. Sebaliknya, Standar C berusaha keras untuk memastikan tidak ada objek berukuran nol yang dibuat: Dalam mendefinisikan malloc (0) dikatakan efeknya menjalankan malloc dengan beberapa argumen tidak nol, misalnya. Dan dalam struct {...; T n []; }; ketika tidak ada ruang yang dialokasikan untuk array (FAM), ia mengatakan berperilaku seolah-olah "n" memiliki satu elemen. (Dalam kedua kasus, menggunakan objek dengan cara apa pun adalah UB, seperti xn [0])
Johannes Schaub - litb
1
Saya pikir sizeof (type)diharapkan untuk tidak pernah mengembalikan nol. Lihat misalnya: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet
Bahkan yang disebut "optimasi kelas dasar kosong" hanya relevan karena desakan bahwa semua objek (sebagai lawan dari semua byte dalam objek) harus memiliki alamat unik. C ++ bisa dibuat lebih sederhana jika kode yang benar-benar peduli benda yang memiliki alamat unik diperlukan untuk memastikan bahwa mereka memiliki ukuran bukan nol.
supercat
15

Apa yang dikatakan standar tentang ini? Apakah selalu legal untuk "mengalokasikan" blok memori kosong?

Setiap objek memiliki identitas unik, yaitu alamat unik, yang menyiratkan panjang non-nol (jumlah memori yang sebenarnya akan meningkat secara diam-diam, jika Anda meminta nol byte).

Jika Anda mengalokasikan lebih dari satu objek ini maka Anda akan menemukan mereka memiliki alamat yang berbeda.

ChrisW
sumber
"Setiap objek memiliki identitas unik, yaitu alamat unik, yang menyiratkan panjang tidak nol" - benar, tetapi salah. Objek memiliki alamat, tetapi penunjuk ke objek dapat menunjuk ke memori acak. "Jumlah aktual memori akan meningkat secara diam-diam, jika Anda meminta nol byte" - tidak yakin. operator []juga menyimpan ukuran array di suatu tempat (lihat isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Jadi jika implementasi mengalokasikan jumlah byte dengan data, itu bisa saja mengalokasikan untuk jumlah byte dan 0 byte untuk data, mengembalikan 1-past-last pointer.
Steed
Panjang yang tidak nol panjang dari memori yang dialokasikan, bukan panjang yang tidak dapat digunakan dari memori yang dapat digunakan. Maksud saya adalah bahwa dua pointer ke dua objek yang berbeda tidak boleh menunjuk ke alamat yang sama.
ChrisW
1
Standar ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ) memang mengatakan (3.7.4.1/2) bahwa panggilan yang berbeda operator new[]harus mengembalikan pointer yang berbeda. BTW, new expressionmemiliki beberapa aturan tambahan (5.3.4). Saya tidak dapat menemukan petunjuk bahwa newdengan ukuran 0 sebenarnya diperlukan untuk mengalokasikan apa pun. Maaf, saya menurunkan suara karena saya menemukan bahwa jawaban Anda tidak menjawab pertanyaan, tetapi memberikan beberapa pernyataan kontroversial.
Steed
@Buat memori untuk menyimpan panjang blok dapat secara implisit dihitung menggunakan ruang alamat. Untuk array berukuran nol misalnya dapat dimungkinkan untuk new[]implementasi untuk mengembalikan alamat dalam rentang di mana tidak ada memori dinamis yang dipetakan, karenanya tidak benar-benar menggunakan memori apa pun (saat menggunakan ruang alamat)
pqnet
@ pqnet: Implementasi kualitas yang baik harus memungkinkan siklus baru / hapus yang tidak terbatas, bukan? Pada platform dengan pointer 64-bit, mungkin masuk akal untuk mengatakan bahwa komputer yang menjalankan while(!exitRequested) { char *p = new char[0]; delete [] p; }loop tanpa pointer daur ulang akan runtuh menjadi debu sebelum bisa kehabisan ruang alamat, tetapi pada platform dengan pointer 32-bit yang akan menjadi asumsi jauh lebih masuk akal.
supercat
14

Ya, itu sepenuhnya legal untuk mengalokasikan 0blok berukuran new. Anda tidak bisa melakukan apa pun yang berguna dengan itu karena tidak ada data yang valid untuk Anda akses.int[0] = 5;itu ilegal.

Namun, saya percaya bahwa standar memungkinkan untuk hal-hal seperti malloc(0)kembaliNULL .

Anda masih perlu delete []pointer apa pun yang Anda dapatkan kembali dari alokasi juga.

Evan Teran
sumber
5
Mengenai malloc, Anda benar - itu adalah implementasi yang ditentukan. Ini sering dianggap sebagai kesalahan.
Saya kira pertanyaan yang menarik adalah: bisakah versi nothrow dari return baru NULL jika diberi ukuran 0?
Evan Teran
1
Semantik baru dan malloc tidak terhubung dengan cara apa pun, setidaknya menurut standar.
tidak mengatakan mereka ... secara khusus bertanya tentang versi nothrow baru.
Evan Teran
@Evan - versi nothrow dari pengembalian baru null hanya jika permintaan gagal - tidak hanya jika ukuran permintaan adalah 0 @ Neil - tidak terkait secara normatif - tetapi dihubungkan dengan niat (mis. Operator baru dapat diimplementasikan dalam hal malloc tetapi tidak yang lain way around) - lihat catatan kaki saya termasuk dalam jawaban saya
Faisal Vali
1

Anehnya, C ++ mengharuskan operator baru mengembalikan pointer yang sah bahkan ketika nol byte diminta. (Memerlukan perilaku yang terdengar aneh ini menyederhanakan hal-hal lain di dalam bahasa ini.)

Saya menemukan Efektif C ++ Edisi Ketiga berkata seperti ini di "Item 51: Patuhi konvensi ketika menulis baru dan menghapus".

shuaihanhungry
sumber
0

Saya jamin Anda bahwa int baru [0] membutuhkan ruang ekstra sejak saya mengujinya.

Misalnya, penggunaan memori

int **arr = new int*[1000000000];

secara signifikan lebih kecil dari

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

Penggunaan memori dari potongan kode kedua dikurangi dari potongan kode pertama adalah memori yang digunakan untuk banyak int baru [0].

Shi Jieming
sumber