Yeni int [0] - bellek ayıracak mı?


241

Basit bir test uygulaması:

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

çıktılar:

0x876c0b8

Yani çalışıyor gibi görünüyor. Standart bu konuda ne diyor? Boş bellek bloğunu "ayırmak" her zaman yasal mıdır?


4
+1 Çok ilginç bir soru - gerçek kodda ne kadar önemli olduğundan emin olmasam da.
Zifre

37
@Zifre: Merak istiyorum, ancak gerçek dünyada önemli olabilir, örneğin tahsis edilen bellek bloklarının boyutu bir şekilde hesaplandığında ve hesaplamanın sonucu sıfır olabilir, o zaman istisna eklemeye doğrudan gerek yoktur sıfır boyutlu bloklar tahsis edilmemelidir .. Çünkü hatasız olarak tahsis edilmeli ve silinmelidirler (eğer sadece sıfır boyutlu blok kaydı kaldırılamıyorsa). Bu genellikle bir bellek bloğunun ne olduğu konusunda daha geniş bir soyutlama sağlar.

2
@ emg-2: Örnek durumunuzda, aslında önemli değil, çünkü delete [] bir NULL işaretçisi için mükemmel bir şekilde yasaldır :-).
Evan Teran

2
Bu sadece teğetsel olarak ilgili - bu yüzden burada yorum yapıyorum - ama C ++ birçok yönden farklı nesnelerin benzersiz adreslere sahip olmasını sağlar ... açıkça depolama gerektirmiyor olsalar bile. İlgili bir deney, boş bir yapının boyutunu kontrol etmek olacaktır. Ya da bu yapının bir dizisi.
Drew Dormann

2
Shmoopty'nin yorumunu detaylandırmak için: Özellikle şablonlarla (örn. Std :: allocator gibi ilke sınıfı şablonları) programlama yaparken, C ++ 'da sıfır boyutlu nesnelerin olması yaygındır. Genel kodun, bu tür nesneleri dinamik olarak tahsis etmesi ve nesne kimliğini karşılaştırmak için onlara işaretçiler kullanması gerekebilir. Bu nedenle new () operatörü sıfır boyutlu istekler için benzersiz işaretçiler döndürür. Tartışmasız daha az önemli / yaygın olmakla birlikte, aynı akıl yürütme dizi tahsisi ve yeni operatör [] () için de geçerlidir.
Trevor Robinson

Yanıtlar:


233

5.3.4 / 7'den itibaren

Direct-new-declarator içindeki ifadenin değeri sıfır olduğunda, eleman içermeyen bir dizi ayırmak için ayırma işlevi çağrılır.

3.7.3.1/2 itibaren

Sıfır boyut için bir istek olarak döndürülen bir işaretçinin kaydının silinmesinin etkisi tanımlanmamıştır.

Ayrıca

İstenen alanın boyutu [yeniye göre] sıfır olsa bile, istek başarısız olabilir.

Bu, bunu yapabileceğiniz anlamına gelir, ancak yasal olarak (tüm platformlarda iyi tanımlanmış bir şekilde) elde ettiğiniz belleği kullanamazsınız - yalnızca dizi silmeye geçirebilirsiniz - ve silmeniz gerekir.

3.7.3.1 / 2'den cümleye eklenmiş ilginç bir dipnot (yani standardın normatif bir parçası değil, ancak ifşa amaçlıdır)

[32. Amaç, malloc () veya calloc () öğesini çağırarak operatöre new () uygulanmasını sağlamaktır, bu nedenle kurallar büyük ölçüde aynıdır. C ++, boş olmayan bir işaretçi döndürmek için sıfır isteği gerektiren C'den farklıdır.]


Silmiyorsam bir bellek sızıntısı oluyor. Bekleniyor mu? En azından beklemiyordum.
EralpB

12
@EralpB: aksine, isteğiniz sıfır olsa bile, bu ayırma Yığın'da gerçekleşir; burada bir istek, ayırıcı tarafından verilen alandan önce ve sonra yığın korumalarını ayırma ve başlatma, freelistlere ekleme veya diğer karmaşık korkunç yapılar. Serbest bırakmak, geriye doğru defter tutma yapmak demektir.
v.oddou

3
@EralpB evet ben bir bellek bir denge yok her zaman sızıntı bekleyebilirsiniz tahmin new[]bir ile delete[]boyutu ne olursa olsun -. Özellikle, aradığınızda new[i], dizinin boyutunu saklamak için delete[]
ayırmaya

24

Evet, bunun gibi sıfır boyutlu bir dizi ayırmak yasaldır. Ancak silmeniz de gerekir.


1
Bunun için bir alıntı var mı? Hepimiz int ar[0];yasadışı olduğunu biliyoruz, neden yeni OK?
Motti

4
İlginçtir, C ++ sıfır boyutlu nesneleri yasaklayan kadar güçlü değildir. Boş temel sınıf optimizasyonunu düşünün: Burada da boş bir temel sınıf alt nesnesinin boyutu sıfır olabilir. Buna karşılık, C Standardı asla sıfır boyutlu nesnelerin oluşturulmadığından emin olmak için güçlü bir çaba sarf etmektedir: Malloc (0) tanımlanırken, etkinin örneğin sıfır olmayan bir argümanla malloc çalıştırması olduğu söylenmektedir. Ve yapı {...; T n []; }; dizi (FAM) için ayrılmış bir alan olmadığında, "n" nin bir elemanı varmış gibi davrandığını söyler. (Her iki durumda da, nesneyi herhangi bir şekilde kullanmak xn [0] gibi UB'dir)
Johannes Schaub - litb

1
Ben düşünüyorum sizeof (type)sıfır asla geri bekleniyor. Örneğin bakınız: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

"Boş temel sınıf optimizasyonu" bile, yalnızca tüm nesnelerin (nesneler içindeki tüm baytların aksine) benzersiz adreslere sahip olması gerektiği konusunda ısrar ettiği için önemlidir. Sıfır olmayan boyutlara sahip olduklarından emin olmak için benzersiz adreslere sahip nesneler hakkında gerçekten bakım gerektiren kod gerekiyorsa, C ++ daha basit hale getirilebilirdi.
supercat

15

Standart bu konuda ne diyor? Boş bellek bloğunu "ayırmak" her zaman yasal mıdır?

Her nesnenin benzersiz bir kimliği vardır, yani sıfır olmayan bir uzunluk anlamına gelen benzersiz bir adres (sıfır bayt isterseniz gerçek bellek miktarı sessizce artar).

Bu nesnelerden birden fazlasını ayırdıysanız, bunların farklı adresleri olduğunu görürsünüz.


"Her nesnenin benzersiz bir kimliği vardır, yani sıfır olmayan bir uzunluk anlamına gelen benzersiz bir adres" - doğru, ancak yanlış. Nesnenin adresi var, ancak nesnenin işaretçisi rastgele belleğe işaret edebilir. "Eğer sıfır bayt isterseniz gerçek bellek miktarı sessizce artacaktır" - emin değilim. operator []ayrıca dizinin boyutunu bir yerde saklar (bkz. isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Dolayısıyla, uygulama bayt sayısını verilerle ayırırsa, yalnızca bayt sayısı ve 0 bayt için veri ayırabilir ve 1-son-son işaretçi döndürür.
Steed

Sıfırdan ayrılan bellek uzunluğu, sıfır olmayan kullanılabilir bellek uzunluğu. Demek istediğim, iki farklı nesneye iki işaretçi aynı adrese işaret etmemeliydi.
ChrisW

1
Standart ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ), farklı çağrıların operator new[]farklı işaretçiler döndürmesi gerektiğini gerçekten (3.7.4.1/2) söylüyor . BTW, new expressionbazı ek kurallara sahiptir (5.3.4). new0 boyutu ile aslında bir şey tahsis etmek için gereken herhangi bir ipucu bulamadım . Üzgünüm, cevap vermedim, çünkü cevabınızın soruları yanıtlamadığını, ancak bazı tartışmalı ifadeler sağladığını fark ettim.
Steed

@ Blok uzunluğu saklamak için bellek garanti adres alanı kullanılarak örtülü olarak hesaplanabilir. Sıfır boyutlu diziler için, örneğin new[]uygulamanın dinamik bellek eşlenmemiş bir aralıktaki adresleri döndürmesi mümkün olabilir , bu nedenle gerçekten bellek kullanmaz (adres alanı kullanılırken)
pqnet

@pqnet: Kaliteli bir uygulama sınırsız sayıda yeni / silme döngüsüne izin vermelidir, öyle değil mi? 64 bit işaretçileri olan bir platformda, while(!exitRequested) { char *p = new char[0]; delete [] p; }geri dönüşüm işaretçileri olmadan bir döngü yürüten bir bilgisayarın adres alanı bitmeden toz haline geleceğini, ancak 32 bit işaretçileri olan bir platformda çok daha az makul varsayım.
supercat

14

Evet, 0büyüklüğünde bir blok tahsis etmek tamamen yasaldır new. Erişebileceğiniz geçerli bir veri olmadığı için bununla yararlı bir şey yapamazsınız.int[0] = 5;yasadışı.

Ancak, standart malloc(0)geri dönmek gibi şeyler için izin verir inanıyorum NULL.

Yine delete []de tahsisattan geri döndüğünüz işaretçiye ihtiyacınız olacaktır .


5
Malloc ile ilgili olarak haklısınız - uygulama tanımlı. Bu genellikle bir yanlışlık olarak görülür.

İlginç bir soru varsayalım: 0 boyutu verilirse yeni dönüşün notch sürümü NULL olabilir mi?
Evan Teran

1
Yeni ve malloc anlambilimi hiçbir şekilde, en azından standart ile bağlantılı değildir.

olduklarını söylememek ... özellikle yeni olanın hileli versiyonunu soruyordu.
Evan Teran

@Evan - yeni isteklerin nothrow sürümü, yalnızca istek başarısız olduğunda null olur - yalnızca istek boyutu 0 @Neil - normatif olarak bağlı değilse - ancak niyetle bağlantılıdır (yani yeni operatör malloc açısından uygulanabilir, ancak diğeri değil ) - cevabıma eklediğim dipnota bakın
Faisal Vali

1

İlginçtir ki, C ++ sıfır bayt istendiğinde bile operatörün yeni bir işaretçi döndürmesini gerektirir. (Bu garip sondaj davranışını gerektirmek, dilin başka bir yerinde işleri kolaylaştırır.)

Buldum Etkili C ++ Third Edition ": Yeni ve sil yazarken uyun sözleşmeye Madde 51" de böyle söyledi.


0

Yeni int [0] 'un test ettiğim için size fazladan alan maliyeti sağladığını garanti ederim.

Örneğin, bellek kullanımı

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

önemli ölçüde daha küçük

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

İkinci kod snippet'inin eksi birinci kod snippet'inin bellek kullanımı, sayısız yeni int [0] için kullanılan bellektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.