Şimdiye kadar yazdığınız her dizinin boyutu derleme zamanında pişirilmişti: int scores[10];, üçe de üç yüze de ihtiyacınız olsa on yuva ayırır. Bu, boyut yalnızca program çalışırken bilinene dek işe yarar - bir dosyanın kaç satırı olduğu, kullanıcının kaç kayıt girdiği. Dinamik bellek bunu çözer: ihtiyacınız olan baytları, ihtiyacınız olduğunda istersiniz ve işiniz bitince geri verirsiniz.
Araçlar <stdlib.h> içindedir:
#include <stdlib.h>
Öbek Neden Var
C, programınıza iki ana bellek bölgesi verir. Yerel değişkenler otomatik depolamada (yığın/stack) yaşar: boyutlarına derleyici karar verir ve fonksiyonları döndüğü anda yok olurlar. Dinamik tahsisler öbekte (heap) yaşar: boyuta çalışma zamanında siz karar verirsiniz ve blok, siz açıkça serbest bırakana dek canlı kalır.
O ikinci özellik, malloc kullanmanın asıl sebebidir. Bir fonksiyon bir blok tahsis edip göstericiyi döndürebilir ve bellek çağıranda hâlâ geçerlidir - yerel bir dizinin asla yapamayacağı bir şey. İki bölge ayrıntılı olarak yığın ve öbek sayfasında karşılaştırılıyor.
malloc: Bayt İstemek
malloc bir bayt sayısı alır ve o kadar ilk değersiz belleğe işaret eden bir void * döndürür; istek başarısız olursa NULL döndürür.
O kısa programdaki dört şey disiplinin tamamıdır:
- Boyut ifadesi
n * sizeof *scores'tur. Bunu "scoresneyi gösteriyorsa ondan n tane" diye okuyun. - Sonuç, herhangi bir kullanımdan önce
NULLile karşılaştırılır. - Blok tam olarak bir dizi gibi kullanılır -
scores[i]çalışır, çünkü indeksleme gösterici aritmetiğidir. - Bir kez serbest bırakılır ve gösterici ardından
NULLyapılır.
Dönüş değerinde dönüşüm olmadığına dikkat edin. C'de void * her nesne göstericisine otomatik dönüşür ve (int *)malloc(...) yazmak gürültü eklerken eksik bir <stdlib.h> include'unu gizler. (C++ dönüşümü ister; C istemez.)
sizeof Deyimi
Neden sizeof(int) değil de sizeof *scores? Çünkü bayatlayamaz.
int *a = malloc(n * sizeof *a); /* n adet int, int burada neyse */
long *b = malloc(n * sizeof *b); /* aynı satır biçimi, doğru boyut */
/* kırılgan biçim */
long *c = malloc(n * sizeof(int)); /* derlenir, çok az tahsis eder */
Son satır, hiçbir derleyicinin uyarmadığı gerçek bir hatadır: c bir long *'dır, tahsis int için boyutlanmıştır ve ilk yarıdan sonraki her yazma sonun dışına taşar. sizeof *c ile boyut bildirimi otomatik izler.
sizeof *p, p'yi dereferans etmez - sizeof yalnızca tipten, derleme zamanında değerlendirilir, dolayısıyla p ilk değersiz ya da NULL olduğunda bile güvenlidir.
İlk Değersiz Bellek
malloc size verdiğini temizlemez. Baytlar daha önce orada ne varsa onu tutar.
Bloğun sizin için sıfırlanmasını istediğinizde calloc bunu tek adımda yapar - bkz. calloc ve realloc.
free: Geri Vermek
free(p) bloğu tahsis ediciye geri verir. Üç kural:
malloc'un döndürdüğü tam göstericiyi geçirin, ilerletilmiş birini değil.free(p + 1)tanımsız davranıştır.- Her bloğu tam olarak bir kez serbest bırakın. İki kez, tahsis edicinin kendi kayıtlarını bozan bir çifte serbest bırakmadır.
- Serbest bıraktıktan sonra gösterici bayattır. Onu kullanmak bir serbest-bırakma-sonrası-kullanımdır.
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* serbest-bırakma-sonrası-kullanım - tanımsız davranış */
free(p); /* çifte serbest bırakma - tanımsız davranış */
Hiçbir satırın hemen çökmesi gerekmez, tehlikeli kılan da budur: program dakikalarca çalışıp sonra alakasız bir yerde başarısız olabilir. Ucuz savunma, göstericiyi boşaltmaktır:
free(p);
p = NULL;
free(NULL)'ın hiçbir şey yapmadığı açıkça tanımlıdır, dolayısıyla ondan sonraki ikinci bir free(p) zararsızdır ve *p, sessiz bozulma yerine gerçek hata noktasında anlık bir çökmeye dönüşür.
Baştan Sona Bir Dinamik Dizi
İşte gerçek bir tahsisin biçimi: çalışma zamanında bir sayı oku, bloğu ondan boyutlandır, kullan, serbest bırak.
Sayıdaki size_t dönüşümü, boyutlar büyüdüğünde önem kazanır: düz bir int n ile n * sizeof *values, çarpma malloc'a ulaşmadan taşabilir ve amaçlanandan çok daha küçük bir blok üretebilir. size_t içinde çarpmak bunu önler.
Bir Fonksiyonun İçinde Tahsis
Öbeğin ömür kuralı, bunu geçerli - ve yerel bir dizi döndürmeyi geçersiz - kılan şeydir:
len + 1, boş sonlandırıcı için yer bırakır - metinlerdeki her yerdeki boyutlandırma kuralının aynısı. Ve fonksiyonun üstündeki yoruma dikkat edin: bir fonksiyon tahsis edilmiş bellek döndürdüğünde, onu kimin serbest bırakacağı sözleşmesinin bir parçasıdır. Bunu yazıya dökmek evrak işi değildir; bir sızıntıyı önleyen tek şeydir.
Öbekteki Yapılar
Aynı deyim her tür için çalışır:
İkinci başarısızlık dalının, dönmeden önce p'yi serbest bıraktığına dikkat edin. Bir fonksiyon birden fazla tahsis tuttuğunda, her hata yolunun çoktan alınmış olanları serbest bırakması gerekir - bellek sızıntıları sayfasında anlatılan sızıntıların en yaygın kaynağı.
Kontrol Listesi
- İlk kullanımdan önce
malloc'u her zamanNULLiçin kontrol edin. n * sizeof *pile,size_tiçinde çarparak boyutlandırın.- C'de dönüş değerinde dönüşüm yoktur.
mallocbelleği ilk değersizdir; okumadan önce yazın.- Tam olarak bir kez, özgün göstericiyle serbest bırakın, sonra
NULLyapın. - Bir tahsisten sonraki her hata yolu, çoktan tuttuklarını serbest bırakmalıdır.
Sıkça Sorulan Sorular
C'de malloc ne yapar?
malloc(n), öbekten n bayt bellek ister ve o bloğun başlangıcına bir gösterici döndürür; istek karşılanamazsa NULL döndürür. Blok, aynı göstericiyi free'ye geçirene dek yaşar - yerel bir değişkenin aksine fonksiyon döndüğünde serbest bırakılmaz.
C'de malloc ve free nasıl kullanılır?
int *p = malloc(n * sizeof *p); ile tahsis edin, ona dokunmadan önce if (p == NULL) kontrolü yapın, bir dizi gibi kullanın, sonra işiniz bittiğinde tam olarak bir kez free(p); yapın. Sonrasında p = NULL atamak, daha sonraki kazara bir kullanımı sessiz bozulma yerine temiz bir çökmeye dönüştürür.
malloc(n * sizeof *p) neden malloc(n * sizeof(int))'ten iyidir?
Çünkü sizeof *p, göstericinin tipini otomatik izler. p sonradan bir long * ya da struct Point * olursa tahsis boyutu kendini günceller; sizeof(int) yazmak, artık yanlış olan ve derleyicinin işaret etmeyeceği bir boyut bırakır.
C'de belleği serbest bırakmazsanız ne olur?
Blok, sürecin ömrü boyunca tahsis edilmiş kalır - bir bellek sızıntısı. Kısa bir program bundan sıyrılır, çünkü işletim sistemi çıkışta her şeyi geri alır; ama uzun süre çalışan bir program her geçişte biraz sızdırır ve sonunda belleği tüketir. Bkz. bellek sızıntıları.