Merhaba, hoş geldiniz. Artık dinamik bellek tahsisini anladığımıza
göre, bunu uygulamalıyız, c kodunda nasıl yapılacağını gerçekten bilmeliyiz.
Şimdi üç önemli fonksiyondan bahsedeceğiz:
malloc, calloc, realloc.
Bellek tahsisinin calloc ve reallocation olarak
adlandırıldığını görürsünüz.
Bellek ayırmanın en basit ve en yaygın yolu malloc
adlı standart kütüphane işlevidir.
Bu, çalışma zamanında dinamik bellek ayırmaktır.
Bu fonksiyonu çağırmak için standart lib başlık dosyasını dahil etmeniz gerekir.
Argüman olarak ayrılmasını istediğiniz bellek bayt sayısını belirtirsiniz.
Bu fonksiyondan döndürülen, ayrılan belleğin ilk baytının adresidir.
Dönen bir adres aldığınız için, onu yerleştirebileceğiniz tek yer bir işaretçidir.
İşte bu yüzden dinamik bellek tahsisi yalnızca bu işaretçi kavramı
aracılığıyla mümkündür çünkü adresleri döndürür.
Bir işaretçiye yalnızca bir adres koyabilirsiniz, işaretçinin sakladığı şey budur, bir işaretçi bir adres saklar.
İşte bunu nasıl yapacağınız, çok basit sözdizimi.
Genellikle işaretçinizi oluştururken buna gerek duymazsınız.
İşaretçinizi oluşturabilir ve null olarak ayarlayabilirsiniz.
Ve bir sonraki satırda malloc yapabilirsiniz.
Ancak burada standart sözdizimi int işaretçinizi, int türünde bir işaretçiyi
oluşturuyorsunuz.
Ve sonra malloc'u çağırdığınızı fark edeceksiniz.
Ve burada, malloc diyoruz, malloc için 100 bayt oluşturacağız
ve sonra bunu bir int işaretçisine atayacağız.
malloc yalnızca bir adres döndürdüğünden, bu adres bir int işaretçisi
depoladığından onu bir uç işaretçisine dönüştürmeniz
gerekir.
Bunu yapmanın standart yolu budur.
Şimdi 100 bayt bellek talep ediyorsunuz
ve bunu bellek bloğunun adresine p numarasına atıyorsunuz.
pNumber, ayrılan 100 baytın başındaki ilk int konumuna işaret edecektir.
Bunun anlamı, tamsayıların her biri 4 bayt olduğundan, 25 int
değeri tutabileceğidir.
Ve yine, bu 4 baytınız olduğunu varsayıyor.
Çalıştırdığınız sisteme bağlı olarak bunlar daha büyük ya da daha küçük olabilir.
Bu yüzden bununla ilgili sorunu görmelisiniz. Bu kod farklı sistemlerde çalışmayacaktır
çünkü int boyutu varsayımına dayalı olarak bir varsayıma göre ayırma
yapıyorsunuz.
Bu yüzden malloc'u bu şekilde 100 sabit sayısı
ile kullanmak gerçekten iyi değildir.
Yapmak istediğiniz şey sizeof operatörünü kullanmaktır.
Bu nedenle, ints'in 4 bayt olduğu varsayımını kaldırmak daha iyi olacaktır.
Bunun yerine, malloc argümanının içinde 25 tamsayı için yer ayırmak istiyorsanız,
ayırmak istediğiniz tamsayı
sayısını belirtin ve ardından bunu tamsayıların boyutuyla çarpın.
Bu da kodu çok daha taşınabilir hale getirecektir.
Bu kod artık farklı sistemlerde, farklı işletim sistemlerinde, farklı mimarilerde çalışacaktır.
Dönüş değerini hala adrese atarsınız çünkü int türünde bir adresi göstermesi
gerekir, bu yüzden int star dersiniz. Buradaki tek fark sizeof operatörünü kullanmanızdır.
Sizeof operatörü, malloc kullanarak bellek ayırırken ya da calloc veya realloc kullanırken
gerçekten önemlidir.
Her zaman ayırdığınız tür için sizeof operatörünü
kullanırsınız ve kullanmak istersiniz ve sonra bunu kaç tane belirli
sayı istiyorsanız onunla çarparsınız, yani 25 int saklamak
istiyorsanız, 25 yaparsınız.
Dolayısıyla, malloc'un yukarıdaki argümanı, int türündeki 25 değeri barındırmak için yeterli
baytın mevcut olması gerektiğini açıkça göstermektedir.
Ayrıca, bahsettiğim gibi, oyuncu kadrosuna dikkat edin. Bu, fonksiyon tarafından int
ve int pointer tipinde pointer'a döndürülen adresi dönüştürür.
Malloc void türünde bir işaretçi döndürür, onu bu kadar kullanışlı yapan da budur.
Bu adresi döndürür ancak bu bir void işaretçisidir.
Hatırlayın, void işaretçilerden ve void işaretçilerin neden yararlı olduğundan
bahsetmiştik çünkü artık farklı parametreler alabilen ve bir void işaretçi döndürebilen bir fonksiyona
sahip olabilirsiniz. Şimdi yine bir void işaretçi döndürmenin
gücünü görüyorsunuz, ancak bunu kullanmak
için, ona gerçekten veri atamak için, onu referans dışı bırakmak için, onu cast etmeniz gerekir.
Eğer bunu yapmak istemeseydik, void star p number diyebilirdik.
Döküm yapmıyoruz, ancak referansı kaldırmak istediğimizde eninde
sonunda döküm yapmak zorunda kalacağız.
Ama bu yüzden malloc'tan döküm yapmamız gerekiyor. Bir adres döndürüyor ve bunun bir int türüne işaret etmesini
istiyorsunuz ve bir void türü döndürüyor.
İstediğiniz sayıda bayt talep edebilirsiniz.
Ancak, bu sisteminize ve sisteminizde ne kadar bellek olduğuna bağlıdır.
Dolayısıyla, bir ton bayt oluşturmaya veya talep etmeye çalışırsanız ya da bazı
sınırlamalar varsa, başarısız olabilir.
Ve başarısız olduğunda, null değerini döndürür, malloc null değerini döndürecektir.
Ve bu iyidir çünkü işaretçiler null'u gösterdiğinde, herhangi bir belleği göstermedikleri
anlamına gelir, böylece herhangi bir belleği
yanlışlıkla bozamaz veya değiştiremezsiniz.
Bu nedenle, malloc yaptıktan sonra kullanmadan önce
null olup olmadığını hemen kontrol etmek her zaman iyi
bir fikirdir, tıpkı veri atadığınızda
veya bir işaretçiyi de-reference ettiğinizde olduğu gibi.
Gerçekten bir şeye işaret ettiğinizden emin olmak istersiniz.
Yani burada, işaretçimiz p numarası için bir miktar bellek ayırıyoruz
ve sonra if not p number diyoruz.
Unutmayın, sıfır sıfırdır.
Yani üzerinde bir not kullanabilirsiniz. Aslında null ile karşılaştırmak zorunda değilsiniz.
Yapabilirsiniz, null ile karşılaştırabilirsiniz, bu onu daha okunabilir kılar, belki daha açık hale getirir.
Ancak sadece p sayısı değil diyebilirsiniz ve bu aslında bir değil
demektir ve null sıfırdır. Yani bu null olup olmadığını kontrol edecektir.
Ve eğer null ise, bu temel olarak belleğin tahsis
edilemediği anlamına gelir. Eğer programınızda bellek ayıramıyorsanız, başınız dertte demektir.
Dolayısıyla, bu kodda bellek ayırma hatasıyla başa çıkmak için
genellikle yapmanız gereken şey iptal etmek, exit'i
çağırmak ve programınızdan çıkmaktır. Çünkü programınız bellek ayıramıyorsa, programınız iflas etmiş demektir.
Yani genellikle bu, bellek ayıramazsanız kaza iptalini çağırmanızdır.
En azından bunu yaptığınızda, programı sonlandıran bir mesaj görüntüleyebilirsiniz.
Bellek
ayıramadığınız zaman programın devam etmesine izin vermekten çok daha iyidir.
Çünkü daha sonra bu belleği kullanmaya devam edebilirsiniz, null
olacaktır, bu nedenle program her halükarda çökecektir ancak nedenini
bilmeniz gerekmeyecektir.
Çağrı yığınına bakmalısınız. Bu hatayı ayıklamak çok daha uzun sürecektir.
Hafızayı serbest bırakıyorum. Şimdi, programcı olarak belleği tahsis
etmekten sorumlu olduğunuz için, artık kullanmadığınızda bellekten kurtulmanız
gerektiğinden bahsettim çünkü bellek, özellikle gömülü sistemlerde hayati
bir kaynaktır.
İhtiyacınız yoksa bellek kullanmamalısınız. Bu yüzden hafızayı serbest bırakmalısınız.
Artık gerekli olmadığında belleği her zaman serbest bırakmak istersiniz.
Yığın üzerinde ayırdığınız bellek, programınız var olduğunda otomatik olarak serbest bırakılacaktır, bu iyi
bir şeydir çünkü bazı temizlik işlemleri yapar.
Ancak çıkış yapmadan hemen önce bile olsa, işiniz bittiğinde belleği
açıkça serbest bırakmak çok daha iyidir.
Daha temizdir, her zaman
free yapın veya belleği ayırdıktan sonra her zaman serbest bırakın,
bu kurala uymak istersiniz. Bu çok katı bir kuraldır.
Bellek ayırırsanız her zaman serbest bırakın.
Bellek sızıntısı, dinamik olarak bir miktar bellek ayırdığınızda ve referansı saklamadığınızda,
böylece onu serbest bırakamadığınızda meydana gelir.
Belki de yapamayacağınız bu referansın kapsamı dışındasınızdır. Onu bırakmayacaksın.
Buna bellek sızıntısı denir. Bellek ayrılmış ancak artık kullanılmıyor.
Bu genellikle bir döngü içinde olduğunuzda ve döngü içinde bellek ayırdığınızda ortaya çıkar.
Sen de işin içindesin. Bu belleği siz ayırırsınız. Döngü içinde kullanırsınız
ve sonra döngüden atlarsınız ve artık serbest bırakmazsınız.
Her döngü iterasyonu onu tahsis ediyor.
Dolayısıyla, dinamik olarak ayırdığınız belleği boşaltmak
için o adrese hala erişiminizin olması gerekir.
Bu nedenle, genellikle belleği kullanmayı bitirdiğinizde ve genellikle ayırdığınız
yöntemle belleği boşaltmak istersiniz.
Ancak, bu belleği başka bir işleve aktarıyorsanız ve bu o işlevin sorumluluğundaysa,
genellikle belleği ve onu ayırdığınız işlevi serbest bırakmak istersiniz.
Adresini bir işaretçide sakladığınız dinamik olarak ayrılmış
bir bellek bloğunun belleğini serbest bırakmak için tek yapmanız gereken
free işlevini çağırmaktır.
Bu standart kütüphanede de yer almaktadır. Sadece free fonksiyonuna işaretçi gönderirsiniz.
Ve genellikle, bu işaretçiyi geçirdikten sonra, null olarak ayarlamak istersiniz
çünkü bu, onun için ayrılmış bir belleğiniz olmadığını gösterir.
free işlevinin void star türünde bir biçimsel parametresi vardır,
bu nedenle herhangi bir işaretçi türü aktarabilirsiniz
ve çalışacaktır.
Gerçekten döküm yapmanız ya da buna benzer bir şey yapmanız gerekmiyor.
p numarası bellek tahsis edildiğinde döndürülen adresi içerdiği sürece, tüm bellek bloğu daha
sonra kullanılmak üzere serbest olacaktır. Diğer programlar bu belleği kullanabilir, programınız
artık bu belleği kullanabilir, gömülü bir
sistemde olduğunuzda önemlidir.
Daha önce de belirttiğim gibi, işaretçinin artık hiçbir şeye işaret etmediğini
belirtmek için işaretçiyi her zaman null olarak ayarlamak istersiniz.
O anı serbest bırakıldı.
calloc adında başka bir güzel fonksiyon daha vardır.
Bunun malloc'a göre birkaç avantajı vardır. Malloc'a çok benzer, ancak yaptığı
şey belleği belirli bir boyutta bir dizi öğe olarak ayırmak ve aynı zamanda
belleği başlatmaktır.
Aradaki fark bu, başlatma. İlklendirmenin öneminden bahsetmiştim.
Bu yüzden her zaman verileri
başlatmak istersiniz, böylece orada beklediğiniz verilerin olduğunu
anlarsınız.
Verilerin ilklendirmesini kaldırdığınızda, orada hakkında hiçbir fikriniz olmayan veriler olabilir.
Bu yüzden verilerinizi her zaman bir şeyle başlatın.
Ve calloc biraz daha iyidir çünkü bunu sizin için yapar.
İki argüman alır, iki parametresi vardır.
Alan gereken veri öğelerinin sayısı, her bir veri öğesinin boyutu.
Standart lib başlığında beyan edilir.
Tıpkı malloc'u çağırdığınız gibi çağırırsınız. Hemen hemen aynı şekilde görünüyor.
Bu örnekte, bir tamsayı işaretçisi oluşturuyoruz
ve sizeof int değerinde 75 tamsayı ayırıyoruz.
Bu yüzden iki parametre alır.
Biraz daha temiz çünkü aslında çarpma işlemi yapmak zorunda değilsiniz.
Malloc'ta 75 zaman boyutu olayı yapmak zorundaydınız.
Burada sadece 75 diyorsunuz,
75 ints oluşturuyorsunuz ve her birinin boyutu 4 bayt veya sistem
size of operatöründen ne döndürürse odur.
Ancak yine de bunun önemi, tüm bu verileri de başlatacak olmasıdır, bu belleği
tahsis edemezse dönüş değeri null olabilir ve bu da iptal etmemeniz gerekir.
Ancak malloc kullanımına çok benzer ancak en büyük artısı bu başlatma işlemidir.
Tüm ints sıfır olarak başlatılır ve bu gerçekten büyük bir anlaşma, büyük
bir avantajdır.
Ayrıca iki parametre ile kullanımı biraz daha kolaydır.
Şimdi de realloc adlı başka bir fonksiyondan bahsedelim.
realloc işlevi, daha önce malloc veya calloc kullanarak ayırdığınız belleği yeniden
kullanmanıza veya genişletmenize olanak tanır.
Yani bu, siz çalışırken ne kadar bellek ayrılacağını dinamik olarak değiştirmenin
bir yoludur.
Yani başlangıçta çok fazla bellek yaratabilirsiniz ve sonra bunun yeterli
olmadığını fark edersiniz. Burada yapabileceğiniz şey sadece realloc'u çağırmaktır
ve bu belleği yeniden tahsis edecektir.
İki argüman değeri bekler, daha önce malloc çağrısı ile
çevrilmiş bir adresi içeren bir işaretçi.
Yani, bir miktar bellek tahsis edilmiş veya calloc edilmiş bir işaretçiniz ve ardından
tahsis etmek istediğiniz yeni belleğin boyutu ve baytları var.
Yani bu, ikinci argümanla belirttiğiniz bellek miktarını tahsis
edecektir, bu ikinci argümandır. İşaretçi tarafından referans verilen önceden ayrılmış
belleğin içeriğini aktarır, böylece işaretçiyi ilettiğiniz için daha önce ne kadar
bellek ayırdığınızı otomatik olarak bilir.
Ve sonra aslında yeni tahsis edilen belleği buna ekleyecektir.
Ve yeni belleğe bir void işaretçi döndürür.
Bu nedenle, realloc'u çağırdığınızda tıpkı diğerlerinde olduğu gibi yeniden yazmanız gerekir.
Yeniden tahsis edemediyseniz null döndürür, bu da muhtemelen
ölümcül bir hatadır ve iptal etmeniz gerekir.
Bu işlemin en önemli özelliği, realloc'un orijinal
bellek alanının içeriğini korumasıdır, sadece bu belleği genişletirsiniz.
Parçalanmış olabilir, çünkü bunu sırayla yapamamış olabilirsiniz, ancak
şu anda verilere atayabileceğiniz tüm belleğe hala sahipsiniz.
Bir örneğe bakalım.
Bu örnekte, malloc kullanacağız ve ardından belleği yeniden tahsis edeceğiz ve ardından
serbest bırakacağız.
Burada bir karakter işaretçisi oluşturuyoruz.
Onu null olarak başlatmıyoruz, muhtemelen öyle yapmalıyız.
Ancak işaret ettiği şey için ayrılmış herhangi bir belleği yoktur.
Sadece bir adres olan işaretçiyi saklamak
için ayrılmış belleği vardır. Ancak bu adrese gitmeye çalışırsanız, programınız ana bilgisayar olacaktır.
Yani yapacağınız şey malloc'u çağırmaktır.
Burada malloc 15 diyoruz.
Bu, bir karakter işaretçisi için 15 bayt oluşturacaktır.
Bir void işaretçi döndürdüğü için onu cast ediyoruz. Yani str artık işaret ettiği
şey için bellek ayrılmış bir işaretçidir.
Daha sonra str copy'yi kullanarak işaret ettiği şeye, bir dizeye, bazı veriler
aktarabiliriz ve adresteki
dizeyi yazdırırız.
O zaman 15 bayt ayırdığımız şeyin yeterince büyük olmadığını
fark ederiz.
Sonra realloc'u çağırırız ve str olan işaretçiyi
geçiririz ve 25 bayt daha yeniden ayırmak isteriz ve sonra tekrar döküm yapmamız gerektiğinden
bu bir void noktası döndürür.
Şimdi hala Jason'ın anısına sahip olacağız.
Şimdi string concatenated diyebiliriz ve com dizesiyle birleştiririz,
böylece şimdi Jason olur. com'a gönderiyoruz ve sonra çıktısını alıyoruz.
Ve son olarak, muhtemelen bu slayttaki
en önemli şey, bellek sızıntısı olmaması için free diyoruz.
malloc'u çağırdığınızda her zaman free'yi çağırın.
Şimdi bu örneğe kod blokları içinde bir göz atalım.
İşte kod blokları editörümüzün
içindeki kod örneğimiz.
Fark etmeniz gereken ilk şey, standart lib başlık dosyasını
dahil ettiğimizdir çünkü malloc fonksiyonlarımız, realloc fonksiyonlarımız
ve free fonksiyonumuz burada yer almaktadır.
Ayrıca bazı str fonksiyonlarını kullandığımız için, birleştirme ve kopyalama,
string'i dahil ediyoruz. h.
Şimdi sekizinci satırda, karakter işaretçimizi oluşturacağız.
Bunun için ayrılmış bir bellek yoktur.
Henüz hiçbir şeye işaret etmediği için
bunu null olarak başlatmalıyız. 11. satırda malloc'u çağırıyoruz.
Ve muhtemelen char'ın bayt boyutunun farklı
olması durumunda char'ın boyutunun 15 katı
demeliyiz ve sonra malloc bir void döndürdüğü için
bunu bir karakter işaretçisine
dönüştürmeliyiz. Daha sonra kopyalayacağız.
Eğer bunu kopyalamaya çalışırsak ve 11. satırda hiçbir şey yapmazsak, program
çöker çünkü string'in işaret ettiği şey için ayrılmış bir belleğimiz yoktur.
Daha sonra sadece dizeyi ve adresi yazdırırız.
Şimdi realloc diyoruz.
Bizim 15 bayttan daha fazla veri depolamamız
gerekiyor, bu yüzden 25 bayt ile yeniden
tahsis edeceğiz ve muhtemelen tutarlı olmak için tekrar sizeof char yapmak istiyoruz.
Bunu atıyoruz ve sonra jason'ı içeren mevcut bellek konumumuzla
birleştiriyoruz. ve Jason'ın çıktısını alacağız. com.
Ve son olarak, 20. satırda, gerçekten önemli olan bu belleği
boşaltıyoruz.
O zaman devam edelim ve bunu çalıştıralım. Ve göreceğiniz gibi, ikinci kez yazdırdığımızda,
birleştirme işlemini gerçekleştirmiş olacağız.
İlk dizemiz için
bellek ayrılmıştır ve adresi 000C2918'dir.
Ardından, argüman olarak ilettiğimiz işaretçinin mevcut boyutunu yeniden tahsis ederek ve alarak
yeni bir adres oluşturduk.
Yani 000C0CD8 aslında kendisine ayrılmış daha
fazla veriye sahiptir, jason'daki
mevcut veri ve 25 bayt daha.
Ve böylece birleştirme işleminin işe yaradığını fark edersiniz.
Çünkü yeniden tahsis ettiğimizde,
jason'ın içeriğini bu belleğe koyduk ve onu genişlettik.
Dolayısıyla bu malloc, calloc ve free realloc çağrılarını kullanmanın pek çok avantajı vardır.
Bunları kullanmak istiyorsunuz, bunları anlamak istiyorsunuz. Kullanımı oldukça basit fonksiyonlar.
Son slaytımızda bazı kılavuz ilkelerin üzerinden geçelim.
Çok sayıda küçük miktarda bellek ayırmaktan kaçınmak istersiniz, bu verimli değildir.
Yığın üzerinde bellek ayırdığınızda,
bunun bazı ek yükleri vardır.
Ve küçük bellek blokları ayırırsanız, daha az sayıda büyük blok ayırmaktan çok daha
fazla ek yüke sahip olursunuz.
Bu yüzden ihtiyacınız olan belleği ayırın, ancak
bunu her iki deyimde bir yeniden tahsis ederek yapmayın, büyük parçalar halinde
yapın.
Belleğe sadece ihtiyacınız olduğu sürece tutun. Hafıza kıt bir kaynaktır.
Yığın halindeyken daha da az bulunur.
Yani bir bellek bloğuyla işiniz bittiğinde free deyin.
Her zaman ayırdığınız belleğin serbest bırakılmasını sağladığınızdan emin olun, belleği
ayıran kodu yazarken kodunuzun neresinde serbest bırakacağınıza karar verin.
Bu işaretçiye erişiminiz olduğundan emin olun.
İşaretçiyi global hale getirmeniz gerekiyorsa, bunu yapmanız gerekir.
Ancak genellikle o işlevde nereye ayırırsanız, o işlevde serbest bırakmanız gerekir.
Sizin için hafızayı serbest bırakan başka işlevleriniz
varsa, bunun iyi olmadığını varsayarsınız. Orada çok fazla bağlantı ve bağımlılık var.
Onu oluşturan kişi, onu silen kişi olmalıdır.
Sorumluluğu diğer işlevlere ve benzerlerine yüklemeyin.
Serbest bırakmadan önce heap üzerinde ayırdığınız belleğin adresinin üzerine yanlışlıkla
yazmadığınızdan emin olun, bu bir bellek sızıntısına neden olacaktır.
Bu nedenle, çok fazla bayt yazmaya çalışırsanız veya taşmalarınız varsa, sorun yaşayabilirsiniz.
Bu nedenle, bir döngü içinde bellek ayırırken özellikle dikkatli olun
çünkü bu, arabellek taşmalarında sorunlara neden olabilir.
Umarım bu yardımcı olmuştur. Dinamik bellek tahsisi büyük, çok büyük bir mesele.
Size bu konuya daha aşina olmanıza yardımcı olacak bir görev vereceğim,
ancak bu kavramı anladığınızdan emin olun. Teşekkür ederim.