Özgür ne kadar özgür olacağını nasıl bilebilir?


385

C programlamasında, istediğiniz argümanı serbestçe bir argüman olarak iletebilirsiniz, boşaltılan belleğin boyutunu nasıl bilebilir? Ne zaman bir işlev için bir işaretçi geçmek, ben de boyutu geçmek zorunda (yani 10 elemanların bir dizi 10 dizi boyutunu bilmek için bir parametre olarak 10 almak gerekir), ama ben boyutu geçmek zorunda değilsiniz serbest işlev. Neden olmasın ve aynı işlevi kendi dizilerimde kullanarak dizinin uzunluğunun ekstra değişkeninin etrafında alışveriş yapmamı engelleyebilir miyim?


Benzer bir soru: stackoverflow.com/questions/851958/… (bunun oldukça yinelenmediğini söylesem de)
John Carter

Eşleştirme sistemi , her blok içinde yükü olmadan, işaretçi göre anlamaya bunu yapmak için başka bir yoldur.
EvilTeach

Yanıtlar:


349

Aradığınızda malloc(), ayrılacak bellek miktarını belirtirsiniz. Kullanılan bellek miktarı bundan biraz daha fazladır ve en azından bloğun ne kadar büyük olduğunu kaydeden ek bilgi içerir. Bu diğer bilgilere (güvenilir bir şekilde) erişemezsiniz - ne de :-).

Aradığınızda free(), sadece blok ne kadar büyük bulmak için ekstra bilgi bakar.


44
FYI, örneğin BSD, ed malloc_size()boyutundan blok boyutuna güvenilir bir şekilde malloc()erişmelidir. Ancak güvenilir, taşınabilir bir yol yoktur.
laalto

50
Bu ekstra bilgi bloğunun döndürülen işaretçiden önce olduğunu söylemek önemlidir.
Georg Schölly

39
@gs Bu, uygulamaya bağlı. Ancak, evet, genellikle olduğu yer burasıdır.
Falaina

31
free()Programcının malloc()bloğun ne kadar büyük olduğunu doğru bir şekilde bildirmesi gerekiyorsa dehşeti hayal edebiliyor musunuz? Bellek sızıntıları olduğu gibi yeterince kötü.
MusiGenesis

35
Bu bilgiler neden kullanılabilir malloc()ve free()bir dizinin boyutunu saklamanız gerekir? blockSize(ptr)Bilgileri zaten saklıyorlarsa neden böyle bir şey yapmayı mümkün hale getirmiyorlar ?
corsiKa

144

C bellek ayırma işlevlerinin çoğu uygulaması, satır içi veya ayrı olarak her bir blok için muhasebe bilgilerini depolar.

Tipik bir yol (satır içi), hem bir üstbilgi hem de istediğiniz hafızayı minimum boyutta doldurmaktır. Örneğin, 20 bayt istediyseniz, sistem 48 baytlık bir blok ayırabilir:

  • 16 baytlık başlık, özel işaretleyici, sağlama toplamı, sonraki / önceki bloğa işaretçiler vb.
  • 32 bayt veri alanı (20 baytınız 16'nın katlarına doldurulmuştur).

Daha sonra size verilen adres, veri alanının adresidir. Ardından, bloğu serbest bıraktığınızda, verdiğiniz freeadresi alır ve bu adresi veya çevresindeki belleği doldurmadığınızı varsayarak, muhasebe bilgilerini hemen önce kontrol edin. Grafiksel olarak, bu şu çizgiler boyunca olacaktır:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Başlık ve dolgu boyutunun tamamen uygulama tanımlı olduğunu unutmayın (aslında, her şey uygulama tanımlıdır (a), ancak satır içi muhasebe seçeneği yaygındır).

Muhasebe bilgilerinde bulunan sağlama toplamları ve özel işaretçiler genellikle üzerine yazdığınızda veya iki kez serbest bırakırsanız "Bellek alanı bozuk" veya "Çift serbest" gibi hataların nedenidir.

Doldurma (ayırmayı daha verimli hale getirmek için) neden bazen sorun yaratmadan talep ettiğiniz alanın sonundan biraz daha fazla yazabilirsiniz (yine de bunu yapmayın, tanımlanmamış davranış ve sadece bazen çalıştığı için Bunu yapmak iyi değil).


(a) İstediğiniz mallocne olursa olsun (sistemdeki en büyük yapının boyutu buydu) 128 bayt aldığınız gömülü sistemlerde uygulamalar yazdım , 128 bayt veya daha azını talep ettiğiniz varsayılarak (daha fazlası için istekler bir NULL döndürme değeri ile karşılanacaktır). 128 baytlık bir yığın tahsis edilip edilmediğine karar vermek için çok basit bir bit maskesi (yani satır içi değil) kullanıldı.

Geliştirdiğim diğerlerinde 16 baytlık parçalar, 64 baytlık parçalar, 256 baytlık parçalar ve 1K parçalar için farklı havuzlar vardı, yine hangi blokların kullanıldığına veya mevcut olduğuna karar vermek için bir bit maskesi kullandılar.

Her iki seçenek de muhasebe bilgilerinin ek yükünü azaltmayı ve çalıştığımız ortamda özellikle önemli olan mallocve free(serbest bırakırken bitişik blokları birleştirmeye gerek yok) hızını artırmayı başardı .


@paxdiablo Bu, malloc'un bitişik bellek blokları ayırmadığı anlamına mı geliyor?
user10678

2
@ user10678, tek gerçek şartı malloc, başarılı bir durumda, en azından istediğiniz kadar büyük bir bellek bloğu vermesidir . Bireysel bloklar, içindeki elemanlara nasıl eriştiğiniz konusunda bitişiktir, ancak blokların geldiği alanların bitişik olmasına gerek yoktur.
paxdiablo

İlgili soru: Neden malloc / free varyasyonu yok, serbest bırakırken boyutu belirttiğiniz ve bu yüzden boyutu saklaması gerekmiyor?
user253751

@ user253751, çünkü o zaman işaretçinin kendisini, üstünde ve üstünde izlemeniz gereken bir şey daha var . Gereksiz ikisi de ve tehlikeli: void *x = malloc(200); free(x, 500);edilir değil de :-) Her durumda, verimlilik için, biteceğini fiili tampon boyutu büyük olabilir (sadece bu güvenemez).
paxdiablo

@paxdiablo Ayrıca boyutu tutmak için bellek israfını önler.
user253751

47

Gönderen comp.lang.cSSS listesinde: Nasıl boşaltmak için kaç bayt serbest biliyor?

Malloc / free uygulaması, tahsis edildiği gibi her bloğun boyutunu hatırlar, bu nedenle serbest bırakırken boyutu hatırlatmaya gerek yoktur. (Tipik olarak, boyut tahsis edilen bloğa bitişik olarak saklanır, bu nedenle tahsis edilen bloğun sınırları biraz fazla atılırsa işler genellikle kötü kırılır)


2
Bu cevap vermiyor. Soru tam olarak şu: neden bloğun boyutunu güvenilir bir şekilde arayabilir, ancak yine de programcı için bunu yapan bir işlev yok?
Bananach

Bu gerçekten de malloc api için bir uygulama detayıdır ve bu bilgiyi standart bir şekilde (bildiklerime) geri getirecek bir api yoktur. "Sistem" bunu kaydeder ve kullanır free. Belki cevap sizi tatmin etmiyor, ancak genel olarak daha uygulanabilir bilgi ile bir tane alacağınızı sanmıyorum :-)
jdehaan

6

Bu yanıt, free () öğesinin ne kadar bellek ayrılacağını nasıl bilebilir? burada açıkça yinelenen bir soru ile cevap vermem engellendi. O zaman bu cevap şu kopyayla ilgili olmalıdır:


Durumunda için malloc, yığın ayırıcısı saklar orijinalin haritalama için gereken ilgili ayrıntılara, işaretçi döndü freesonradan bellek ing. Bu, tipik olarak, bellek bölgesinin büyüklüğünün, kullanılan ham madde veya tahsisleri izlemek için kullanılan bir ikili ağaçtaki bir düğümü veya kullanımdaki bir bellek "birimi" miktarını, örneğin kullanılan ayırıcı ile ilgili herhangi bir biçimde depolamayı içerir.

freeişaretçiyi "yeniden adlandırırsanız" veya herhangi bir şekilde çoğaltırsanız başarısız olmaz. Bununla birlikte referans sayılmaz ve sadece ilki freedoğru olur. Ek bilgiler free"çift serbest" hatalardır.

freeÖnceki mallocs tarafından döndürülen değerlerden farklı ve henüz çözülmemiş olan herhangi bir işaretçiyi denemek bir hatadır. Geri döndürülen bellek bölgelerini kısmen boşaltmak mümkün değildir malloc.


Bir malloc çağrısı tarafından döndürülen bir işaretçinin değerini değiştirdim. Ve hatasız serbest bıraktım. Neden? Buraya bakın: stackoverflow.com/questions/42618390/…
smwikipedia

4

İlgili bir notta GLib kütüphanesi, örtük boyutu kaydetmeyen bellek ayırma işlevlerine sahiptir - ve sonra sadece size parametresini ücretsiz olarak geçirirsiniz. Bu, ek yükün bir kısmını ortadan kaldırabilir.


3

malloc() ve free() sisteme / derleyiciye bağımlı olduğundan belirli bir cevap vermek zordur.

Bu diğer soru hakkında daha fazla bilgi .


2
Gerçekten kütüphaneye bağımlıdırlar (genellikle işletim sistemine çok yakından bağlı olan C kütüphanesi). Derleyiciye göre bunlar sadece işlevlerdir.
Donal Fellows

2

Yığın yöneticisi, aradığınızda bir yerde ayrılan bloğa ait bellek miktarını depoladı malloc.

Hiç bir zaman kendim uygulayamadım, ama tahsis edilen bloğun hemen önündeki bellek meta bilgileri içerebilir.


3
Bu olası bir uygulama olmakla birlikte, tüm belleğin, ayrılan bellek havuzuna yakın herhangi bir yere değil, tamamen farklı bir sayfada tek bir tabloda izlendiği bir sistem tasarlanabilir.
ephemient

2

Orijinal teknik, biraz daha büyük bir blok tahsis etmek ve başlangıçta boyutu saklamak, daha sonra uygulamaya blogun geri kalanını vermekti. Ekstra alan bir boyutu tutar ve muhtemelen serbest blokları yeniden kullanmak üzere birleştirmek için bağlanır.

Ancak bu hilelerle ilgili zayıf önbellek ve bellek yönetimi davranışı gibi bazı sorunlar vardır. Belleği doğrudan blokta kullanmak gereksiz yere sayfa oluşturma eğilimindedir ve ayrıca paylaşımı ve yazma üzerine kopyalamayı zorlaştıran kirli sayfalar oluşturur.

Yani daha gelişmiş bir teknik ayrı bir dizin tutmaktır. Bellek alanlarının iki boyutun aynı gücünü kullandığı egzotik yaklaşımlar da geliştirilmiştir.

Genel olarak cevap: durumu korumak için ayrı bir veri yapısı tahsis edilir.


1

Sorunuzun ikinci yarısını yanıtlamak için: evet, yapabilirsiniz ve C'de oldukça yaygın bir örüntü şudur:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;

Bu, BSD malloc'un küçük nesneler için kullandığı teknikten tamamen farklı bir tekniktir (Pascal tarzı diziler oluşturmak için mükemmel bir teknik olsa da)
Pete Kirkham

0

Malloc dediğimizde, gerekliliğinden daha fazla bayt tüketir. Bu daha fazla bayt tüketimi, çek toplamı, boyut ve diğer ek bilgiler gibi bilgileri içerir. O zaman ücretsiz aradığımızda, doğrudan adresi bulduğu ve ek blokun ne kadar ücretsiz olacağını bulduğu ek bilgilere doğrudan gider.


0

ikinci soruyu cevaplamak için, evet, malloc() her dizinin içindeki ilk hücrenin dizinin boyutuna atanmasıyla aynı tekniği kullanabilirsiniz . ek bir boyut argümanı göndermeden diziyi göndermenizi sağlar.

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.