Malloc'u C'de ne zaman kullanmalıyım ve ne zaman kullanmam?


94

Malloc () 'un nasıl çalıştığını anlıyorum. Sorum şu, şöyle şeyler göreceğim:

#define A_MEGABYTE (1024 * 1024)

char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);

Kısalık uğruna hata kontrolünü atladım. Sorum şu, bellekteki bazı statik depolamaya bir işaretçi başlatarak yukarıdakileri yapamaz mısınız? belki:

char *some_memory = "Hello World";

Saklamanız gereken değerleri bildirmek / başlatmak yerine, aslında hangi noktada hafızayı kendiniz ayırmanız gerekiyor?


5
Ynt: Kısalık uğruna hata kontrolünü ihmal ettim - ne yazık ki birçok programcı hata kontrolünü ihmal ediyor çünkü malloc()başarısız olabileceğinin farkında değiller !
Andrew

Yanıtlar:


133
char *some_memory = "Hello World";

bir dizge sabitine işaretçi oluşturmaktır. Bu, "Merhaba Dünya" dizesinin hafızanın salt okunur bölümünde bir yerde olacağı ve sizin sadece ona bir işaretçiniz olduğu anlamına gelir. Dizeyi salt okunur olarak kullanabilirsiniz. Sen olamaz buna değişiklikleri yapın. Misal:

some_memory[0] = 'h';

Bela istiyor.

Diğer yandan

some_memory = (char *)malloc(size_to_allocate);

bir char dizisi (bir değişken) ve bazı_memory noktaları bu ayrılmış belleğe ayırmaktır. Şimdi bu dizi hem okuma hem de yazma. Şimdi yapabilirsiniz:

some_memory[0] = 'h';

ve dizi içeriği "merhaba Dünya" olarak değişir


19
Sadece açıklığa kavuşturmak için, bu cevabı ne kadar sevsem de (size +1 verdim), malloc () olmadan sadece bir karakter dizisi kullanarak aynısını yapabilirsiniz. Şuna benzer bir şey: char some_memory [] = "Merhaba"; some_memory [0] = 'W'; da çalışacak.
randombits

19
Haklısın. Bunu yapabilirsin. Malloc () kullandığınızda, bellek çalışma zamanında dinamik olarak tahsis edilir, bu nedenle derleme zamanında dizi boyutunu sabitlemenize gerek yoktur, ayrıca realloc () kullanarak büyütebilir veya küçültebilirsiniz. Bunu yaptığınızda bunlardan hiçbiri yapılamaz: char some_memory [] = "Merhaba"; Burada dizinin içeriğini değiştirebilseniz bile boyutu sabittir. Bu nedenle, ihtiyaçlarınıza bağlı olarak üç seçenekten birini kullanırsınız: 1) işaretçi karakter dizisine 2) dinamik olarak ayrılmış dizi 3) sabit boyutta, zaman ayrılmış diziyi derleyin.
codaddict

Bunun salt okunur olduğunu vurgulamak için yazmalısınız const char *s = "hi";Bu aslında standart tarafından gerekli değil mi?
Theis'e kadar

1
@Till, hayır, çünkü "hi" dizesinin temel adresine başlatılmış bir işaretçi bildirdiniz. s sabit olmayan bir karaktere işaret etmek için yasal olarak mükemmel bir şekilde yeniden atanabilir. const char const* s;
Salt

38

Tam bu örnek için, malloc çok az kullanışlıdır.

Malloc'un gerekli olmasının birincil nedeni, kod kapsamından farklı bir ömrü olması gereken verilere sahip olmanızdır. Kodunuz bir rutinde malloc'u çağırır, işaretçiyi bir yerde saklar ve sonunda farklı bir rutinde ücretsiz olarak çağırır.

İkincil bir neden ise, C'nin yığında bir ayırma için yeterli alan olup olmadığını bilmesinin bir yolu olmamasıdır. Kodunuzun% 100 sağlam olması gerekiyorsa, malloc kullanmak daha güvenlidir çünkü kodunuz tahsisin başarısız olduğunu bilebilir ve halledebilir.


4
Bellek yaşam döngüleri ve bunun ne zaman ve nasıl serbest bırakılacağıyla ilgili soru, birçok ortak kitaplık ve yazılım bileşeninde önemli bir sorundur. Tipik olarak iyi belgelenmiş bir kuralı vardır: " Bu rutinlerimden birine bir işaretçi verirseniz, onu malloc'lamanız gerekir. Onu takip edeceğim ve işim bittiğinde onu serbest bırakacağım. " Kötü hataların yaygın bir kaynağı, böyle bir kitaplığa statik olarak ayrılmış belleğe bir işaretçi iletmektir. Kütüphane onu serbest bırakmaya () çalıştığında, program çöküyor. Son zamanlarda başka birinin yazdığı gibi bir hatayı düzeltmek için çok zaman harcadım.
Bob Murphy

Malloc () 'un pratik olarak kullanıldığı tek zamanın, program ömrü boyunca birden çok kez çağrılacak olan ve malloc'dan beri' temizlenmesi 'gereken bir kod segmenti olduğu zaman mı diyorsunuz? () 'e ücretsiz () eşlik ediyor mu? Örneğin, çarkıfelek gibi bir oyunda, tahmin ettikten ve girdiyi belirlenmiş bir karakter dizisine koyduktan sonra, malloc () büyüklüğündeki dizinin bir sonraki tahmin için serbest bırakılabileceği yer neresi?
Smith Will Suffice

Verilerin ömrü gerçekten de malloc kullanmanın gerçek nedenidir. Soyut bir veri türünün bir modül tarafından temsil edildiğini, bir Liste türü bildirdiğini ve listeden öğe eklemek / silmek için yordamlar olduğunu varsayalım. Bu öğe değerlerinin dinamik olarak ayrılmış belleğe kopyalanması gerekir.
Rob11311

@ Bob: bu iğrenç böcekler, ayırıcının hafızayı çok daha üstün bir şekilde serbest bırakma kuralını yapın, sonuçta onu geri dönüştürüyor olabilirsiniz. Tüm blok için yalnızca bir kez ücretsiz aramanız gerektiğinden, bu kitaplıkların bozuk doğasını ortaya çıkaran referansların yerelliğini iyileştirmek için calloc ile bellek ayırdığınızı varsayalım. Neyse ki, belleğin 'malloc-ed' olduğunu belirten kütüphaneleri kullanmak zorunda kalmadım, bu bir POSIX geleneği değil ve büyük olasılıkla bir hata olarak kabul edilecek. Malloc kullanmanız gerektiğini "biliyorlarsa", neden kütüphane rutini sizin için yapmıyor?
Rob11311

17

malloc, derleme zamanında işlenen ve dolayısıyla boyutu değiştirilemeyen merhaba dünya örneğiniz gibi statik bildirimlerle karşılaştırıldığında, çalışma zamanında belleği ayırmak, yeniden tahsis etmek ve boşaltmak için harika bir araçtır.

Bu nedenle Malloc, dosya içeriklerini okumak veya soketlerle uğraşmak gibi rastgele boyuttaki verilerle uğraşırken ve işlenecek verilerin uzunluğunun farkında olmadığınız zamanlarda her zaman yararlıdır.

Elbette, verdiğiniz gibi önemsiz bir örnekte, malloc sihirli "doğru iş için doğru araç" değildir, ancak daha karmaşık durumlarda (örneğin, çalışma zamanında rastgele boyutta bir dizi oluşturmak), bunun tek yolu budur. Git.


7

Kullanmanız gereken belleğin tam boyutunu bilmiyorsanız, dinamik ayırmaya ( malloc) ihtiyacınız vardır . Bir örnek, bir kullanıcının uygulamanızda bir dosya açması olabilir. Dosyanın içeriğini belleğe okumanız gerekecek, ancak kullanıcı dosyayı çalışma zamanında yerinde seçtiği için tabii ki dosyanın boyutunu önceden bilmiyorsunuz. Dolayısıyla, temel mallocolarak önceden üzerinde çalıştığınız verilerin boyutunu bilmediğinizde ihtiyacınız var . En azından kullanmanın ana nedenlerinden biri bu malloc. Derleme zamanında boyutunu zaten bildiğiniz (ayrıca onu değiştirmek istemediğiniz) basit bir dizeye sahip örneğinizde, bunu dinamik olarak ayırmanın pek bir anlamı yoktur.


Biraz konu dışı, ama ... kullanırken hafıza sızıntısı yaratmamak için çok dikkatli olmalısın malloc. Bu kodu düşünün:

int do_something() {
    uint8_t* someMemory = (uint8_t*)malloc(1024);

    // Do some stuff

    if ( /* some error occured */ ) return -1;

    // Do some other stuff

    free(someMemory);
    return result;
}

Bu kodda neyin yanlış olduğunu görüyor musunuz? Arasında bir koşullu Dönüş ifadesi var mallocve free. İlk başta iyi görünebilir ama bir düşünün. Bir hata varsa, ayırdığınız belleği boşaltmadan geri döneceksiniz. Bu, bellek sızıntılarının yaygın bir kaynağıdır.

Elbette bu çok basit bir örnek ve burada hatayı görmek çok kolay, ancak işaretçiler, mallocs, frees ve her türlü hata işlemeyle dolu yüzlerce satır kod hayal edin . İşler gerçekten çok hızlı bir şekilde dağınık hale gelebilir. Bu, uygulanabilir durumlarda modern C ++ 'yı C'ye tercih etmemin nedenlerinden biri, ancak bu tamamen başka bir konu.

Bu yüzden ne zaman kullanırsanız kullanın malloc, hafızanızın olabildiğince büyük olacağından emin olun free.


Mükemmel örnek! ^ _ ^
Musa Al-hassy

6
char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");

yasa dışıdır, dize değişmezleri const.

Bu, yığın üzerinde veya genel olarak (bildirildiği yere bağlı olarak) 12 baytlık bir karakter dizisi tahsis edecektir.

char some_memory[] = "Hello World";

Daha fazla manipülasyon için yer bırakmak istiyorsanız, dizinin daha büyük boyutta olması gerektiğini belirtebilirsiniz. (Lütfen yığına 1MB koymayın.)

#define LINE_LEN 80

char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);

5

Belleği ayırmanın gerekli olmasının bir nedeni, onu çalışma zamanında değiştirmek istemenizdir. Bu durumda, yığın üzerinde bir malloc veya bir tampon kullanılabilir. Bir işaretçiye "Merhaba Dünya" atamanın basit örneği, "tipik olarak" çalışma zamanında değiştirilemeyen belleği tanımlar.

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.