Malloc vs yeni - farklı dolgu


110

Yüksek performanslı bilgi işlem (10 ^ 5 - 10 ^ 6 çekirdek) için MPI kullanan projemiz için başka birinin C ++ kodunu gözden geçiriyorum. Kodun, farklı mimarilerdeki (potansiyel olarak) farklı makineler arasında iletişime izin vermesi amaçlanmıştır. Şu satırlar boyunca bir şeyler söyleyen bir yorum yazdı:

Normalde newve kullanırdık delete, ama burada mallocve kullanıyorum free. Bu gereklidir çünkü bazı derleyiciler newkullanıldığında verileri farklı şekilde doldurur ve bu da farklı platformlar arasında veri aktarımında hatalara yol açar. Bu ile olmaz malloc.

Bu, standart newve mallocsorulardan bildiğim hiçbir şeye uymuyor .

New / delete ve malloc / free arasındaki fark nedir? derleyicinin bir nesnenin boyutunu farklı şekilde hesaplayabileceği fikrine ipucu verir (ama o halde bu neden kullanmaktan farklıdır sizeof?).

malloc ve yeni yerine yeniyi yerleştirme oldukça popüler bir sorudur, ancak yalnızca newyapıcıların kullanılmadığı yerlerde kullanılması hakkında konuşur malloc, bu da bununla alakalı değildir.

malloc hizalamayı nasıl anlar? hafızanın biriyle doğru şekilde hizalanmasının garantili olduğunu newveya mallocdaha önce düşündüğüm şey olduğunu söylüyor.

Bence o geçmişte kendi gýcýk biraz zaman yanlış teşhis ve sonucuna varılmaktadır oluyor ki newve mallocben doğru değildir muhtemelen düşünüyorum dolgu farklı miktarlarda vermek. Ancak yanıtı Google'da veya önceki soruda bulamıyorum.

Bana yardım et, StackOverflow, tek umudum sensin!


33
Sadece çeşitli SO konularının araştırılması için +1!
iammilind

7
+1 SO'da UZUN bir süredir gördüğüm en iyi "başkalarına sormadan önce kendime yardım et" araştırma işlerinden biri. Keşke buna birkaç kez daha olumlu oy verebilseydim.
WhozCraig

1
Transfer kodu, verinin belirli bir şekilde hizalandığını, örneğin sekiz baytlık bir sınırda başladığını varsayıyor mu? Bu, mallocve arasında farklılık gösterebilir new, çünkü newbazı ortamlarda bir blok tahsis eder, başlangıca bazı veriler ekler ve bu veriden hemen sonra bir konuma bir işaretçi döndürür. (Veri bloğunun içinde, başkalarıyla kabul, mallocve newdolgu aynı tür kullanmalıdır.)
Lindydancer

1
Vay canına , bu sorunun bu kadar popüler olmasını beklemiyordum! @Lindydancer, 8 baytlık bir sınır varsayıldığını sanmıyorum. Yine de ilginç bir nokta.
hcarver

1
Bir tahsis yöntemini diğerine göre kullanmanın bir nedeni, "başka birinin" nesneyi serbest bırakmasıdır. Bu "başka biri" nesneyi free kullanarak silerse, malloc kullanarak ayırmanız gerekir. (Pad sorunu kırmızı ringa balığıdır.)
Lindydancer

Yanıtlar:


25

IIRC bir seçici nokta var. mallocherhangi bir standart tür için hizalanmış bir adres döndürmesi garanti edilir. ::operator new(n)yalnızca n'den büyük olmayan herhangi bir standart tür için hizalanmış bir adres döndürmesi garanti edilir ve Tbir karakter türü değilse new T[n], yalnızca hizalanmış bir adresi döndürmek için gereklidir T.

Ancak bu, yalnızca bayrakları depolamak için işaretçinin alt birkaç bitini kullanmak gibi uygulamaya özgü hileler oynarken veya başka bir şekilde tam olarak ihtiyaç duyduğundan daha fazla hizalamaya sahip olmak için adrese güveniyorsanız geçerlidir.

Kapladığı belleği nasıl ayırdığınıza bakılmaksızın, mutlaka aynı düzene sahip olan nesne içindeki dolguyu etkilemez. Dolayısıyla, farklılığın veri aktarırken hatalarla nasıl sonuçlanacağını görmek zor.

Bu yorumun yazarının, ister "malloc gibi yastıklı", ister "yeni gibi yastıklı" olsun, yığın üzerindeki veya küresel nesneler hakkında ne düşündüğüne dair herhangi bir işaret var mı? Bu, fikrin nereden geldiğine dair ipuçları verebilir.

Belki kafasının karışık olduğunu, ama belki de bahsediyor kod arasındaki düz bir farkın fazla olması malloc(sizeof(Foo) * n)vs new Foo[n]. Belki daha çok şuna benzer:

malloc((sizeof(int) + sizeof(char)) * n);

vs.

struct Foo { int a; char b; }
new Foo[n];

Yani, belki ediyor edilir söyleyerek "Ben malloc kullanmak", ama araçlar "Bunun yerine el bir yapı kullanmanın hizalanmamış konumlara veri paketi". Yapıyı mallocmanuel olarak paketlemek için aslında gerekli değildir, ancak bunun daha az bir kafa karışıklığı olduğunun farkına varmamak. Tel üzerinden gönderilen veri düzenini tanımlamak gerekir. Yapı kullanıldığında farklı uygulamalar verileri farklı şekilde doldurur .


Uyumla ilgili noktalar için teşekkürler. Söz konusu veriler bir karakter dizisi, bu yüzden burada bir hizalama meselesi veya yapısal bir şey olmadığından şüpheleniyorum - yine de ilk düşüncem buydu.
hcarver

5
@Hbcdev: iyi chardiziler hiçbir zaman doldurulmaz, bu yüzden açıklama olarak "kafam karıştı" diyeceğim.
Steve Jessop

5

Meslektaşınızın new[]/delete[]aklında sihirli tanımlama bilgisi olabilir (bu, uygulamanın bir diziyi silerken kullandığı bilgidir). Ancak, geri dönen adresten başlayan tahsis new[]kullanılsaydı (ayırıcıların aksine) bu bir sorun olmazdı .

Paketleme daha olası görünüyor. ABI'lerdeki varyasyonlar (örneğin) bir yapının sonuna eklenen farklı sayıda takip baytı ile sonuçlanabilir (bu, hizalamadan etkilenir, dizileri de dikkate alır). Malloc ile bir yapının konumu belirlenebilir ve böylece yabancı bir ABI'ye daha kolay taşınabilir. Bu varyasyonlar, normal olarak, transfer yapılarının hizalanması ve paketlenmesi belirtilerek önlenir.


2
İlk düşündüğüm şey buydu, "yapı parçalarının toplamından daha büyüktür" problemi. Belki de fikrinin doğduğu yer burasıdır.
hcarver

3

Bir nesnenin düzeni, mallocveya kullanılarak tahsis edilmesine bağlı olamaz new. İkisi de aynı tür bir gösterici döndürür ve bu işaretçiyi diğer işlevlere geçirdiğinizde nesnenin nasıl tahsis edildiğini bilemezler. nasıl tayin edildiğine değil sizeof *ptr, beyanına bağlıdır ptr.


3

Bence haklısın. Dolgu, derleyici tarafından newveya değil yapılır malloc. Kullanmadan newveya hiç kullanmadan bir dizi veya yapı bildirseniz bile dolgu ile ilgili hususlar geçerli olacaktır malloc. Her durumda ben nasıl farklı uygulamaları görebiliyoruz, newve mallocben tamamen onlar sorunlar platformlar arasında veri aktarımı neden olabilir görmek başarısız, platformlar arasında kodu taşıma sırasında sorunlara neden olabilir.


Daha önce newiyi bir paketleyici olarak düşünebileceğinizi varsaymıştım , mallocancak diğer yanıtlardan bu pek doğru değil gibi görünüyor . Fikir birliği gibi görünüyor, dolgu her ikisiyle de aynı olmalıdır; Platformlar arasında veri aktarımıyla ilgili sorunun yalnızca aktarım mekanizmanız kusurlu olması durumunda ortaya
çıktığını düşünüyorum

0

Eski düz veri yapımın düzenini kontrol etmek istediğimde MS Visual derleyicileri kullanıyorum #pragma pack(1). Sanırım böyle bir ön derleyici yönergesi, örneğin gcc gibi çoğu derleyici için destekleniyor .

Bu, yapıların tüm alanlarının boş alanlar olmadan arka arkaya hizalanması sonucunu doğurur.

Diğer uçtaki platform da aynı şeyi yaparsa (yani, veri alışveriş yapısını 1 dolgu ile derlediyse), o zaman her iki taraftan alınan veriler iyi uyuyor. Böylece C ++ 'da malloc ile oynamak zorunda kalmadım.

En kötüsü, malloc'u C ++ 'da doğrudan kullanmak yerine bazı zor şeyleri gerçekleştirdiği için yeni operatörü aşırı yüklemeyi düşünürdüm.


Veri yapısı düzenini kontrol etmek istediğiniz hangi durumlar var? Sadece merak.
hcarver

Ve destekleyen pragma packveya benzer derleyiciler bilen var mı? Standardın bir parçası olmayacağının farkındayım.
hcarver

gcc bunu örneğin destekler. hangi durumda buna ihtiyacım vardı: iki farklı plaka formu arasında ikili veri paylaşımı: pencereler ve palmOS arasında, pencereler ve linux arasında ikili akışı paylaşmak. gcc ile ilgili bağlantılar: gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Stephane Rolland

0

Bu şeyin nereden geldiğine dair çılgın tahminim bu. Bahsettiğiniz gibi, sorun MPI üzerinden veri aktarımı ile ilgili.

Kişisel olarak, MPI üzerinden göndermek / almak istediğim karmaşık veri yapılarım için, her şeyi bir dizi karaktere paketleyen / paketten çıkaran serileştirme / serileştirme yöntemlerini uyguluyorum. Şimdi, doldurma nedeniyle, yapının boyutunun, üyelerinin boyutundan daha büyük olabileceğini biliyoruz ve bu nedenle, kaç bayt gönderildiğini / alındığını bilmemiz için veri yapısının doldurulmamış boyutunu da hesaplamamız gerekiyor.

Örneğin std::vector<Foo> A, söz konusu teknikle MPI üzerinden göndermek / almak istiyorsanız , sonuçta ortaya çıkan karakter dizisinin boyutunun A.size()*sizeof(Foo)genel olarak olduğunu varsaymak yanlıştır . Başka bir deyişle, serileştirme / seriyi kaldırma yöntemlerini uygulayan her sınıf, dizinin boyutunu bildiren (veya daha iyisi diziyi bir kapta depolayan) bir yöntem de uygulamalıdır. Bu , bir hatanın arkasındaki sebep olabilir . Bununla birlikte, bu ileti dizisinde belirtildiği gibi newvs ile hiçbir ilgisi olmayan bir şekilde veya başka bir şey malloc.


Char dizilerine kopyalamak sorunlu olabilir - bazı çekirdeklerinizin küçük-endian mimarilerde olması ve bazılarının büyük endian (belki olası değil ama mümkün) olması mümkündür. Bunları veya başka bir şeyi XDR ile kodlamanız gerekir, ancak yalnızca kullanıcı tanımlı MPI veri türlerini kullanabilirsiniz. Kolaylıkla dolguyu hesaba katarlar. Ama yanlış anlamanın olası nedeni hakkında ne söylediğini anlayabiliyorum - ben buna "yapı parçalarının toplamından daha büyük" problemi diyorum.
hcarver

Evet, MPI veri türlerini tanımlamak, bunu yapmanın başka / doğru yoludur. Bitmekle ilgili iyi bir nokta. Yine de, bunun gerçek kümelerde olacağından gerçekten şüpheliyim. Her neyse, aynı stratejiyi izlerlerse bu hatalara yol açabilir diye düşündüm ...
mmirzadeh

0

C ++ 'da: new anahtar kelime, bazı veri yapılarına göre belirli bellek baytlarını tahsis etmek için kullanılır. Örneğin, bir sınıf veya yapı tanımladınız ve nesnesi için bellek ayırmak istiyorsunuz.

myclass *my = new myclass();

veya

int *i = new int(2);

Ancak her durumda, tanımlanmış veri türüne (sınıf, yapı, birlik, int, karakter vb.) İhtiyacınız vardır ve yalnızca nesnesi / değişkeni için gerekli olan bellek baytları tahsis edilecektir. (yani; bu veri türünün katları).

Ancak malloc () yöntemi durumunda, herhangi bir bayt bellek ayırabilirsiniz ve her zaman veri türünü belirtmeniz gerekmez. Burada birkaç malloc () olasılığında gözlemleyebilirsiniz:

void *v = malloc(23);

veya

void *x = malloc(sizeof(int) * 23);

veya

char *c = (char*)malloc(sizeof(char)*35);

-1

malloc bir işlev türüdür ve yeni, c ++ 'da c ++' da bir tür veri türüdür, olması gerekenden daha malloc kullanırsak ve typecast kullanmalıyız, aksi takdirde derleyici size hata verir ve bellek tahsisi için ihtiyaç duymadığımızdan daha yeni veri türü kullanırsak yazmak için


1
Bence cevabınızı biraz daha tartışmaya çalışmalısınız.
Carlo

Bu, küreklerle farklı şeyler yaptıkları sorusuna hitap ediyor gibi görünmüyor, aslında yukarıda sorduğum da buydu.
hcarver
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.