Ders 11 / 15
Bellek Düzeni
Bir programın belleği tek bir alan değildir; otomatik, statik ve dinamik olmak üzere ayrı bölgelere bölünür ve bir bloğun hangi bölgede durduğu, onun sahibinin kim olacağını baştan belirler. Ders dört dil gözlemini ayir ve birak gözlemiyle yan yana koyup bu ayrımı adres basmadan ölçüyor.
Yayın Güncelleme
Bu derste
- Otomatik, statik ve dinamik bölgelerin sahiplik sınıflarıyla nasıl örtüştüğünü gösterir
- Dört dil gözlemini ayir ve birak gözlemiyle karşılaştırarak sayaç farkının yalnız dinamik bölgede kıpırdadığını ölçer
- Bölge sınırlarını ve hizalama değerlerini basmadan, bölgenin sahiplikle ilişkisini açıklar
İçindekiler
Dinamik Bellek konusunun önceki dört dersi belleğin dinamik bölgesinde kaldı: malloc, calloc,
realloc, sahiplik sözleşmesi, sızıntı, denetim noktası — hepsi çalışma zamanında ayrılan
ve elle bırakılan bloklarla ilgiliydi. Ama bu kursun ilk dersinden beri her ölçümde dil
sınıfı da vardı: yerel değişkenler, statik alanlar, dizgi değişmezleri. Bu ders, konuyu
kapatırken, dinamik bölgeyi geri kalan üç bölgenin yanına koyuyor ve bir bloğun hangi
bölgede durduğunun, sahibinin kim olacağını nasıl baştan belirlediğini gösteriyor.
Dört Bölge, Bir Soru
Bir C programının belleği birden çok bölgeye ayrılır: kodun kendisi, sabit veri, statik değişkenler, çalışma zamanı yığıtı (otomatik değişkenler için) ve öbek (dinamik ayırma için). Bu bölgelerin donanımda nasıl yerleştiği ve aralarındaki sınırların nasıl hizalandığı Bilgisayarlar Nasıl Çalışır kursunda (M01/K01) ele alındı; bu ders o düzeni yeniden anlatmıyor, yalnız sahiplik sorusuyla ilişkilendiriyor. Soru hep aynı: bu bloğu kim bırakacak? Yanıt, bloğun hangi bölgede durduğuna bağlı.
// sayac.h — her ayirmayi ve her birakmayi sayan sarmalayici #ifndef SAYAC_H #define SAYAC_H #include <stddef.h> void *say_ayir(size_t boyut); void *say_buyut(void *eski, size_t boyut); void say_birak(void *blok); int ayrilan_sayisi(void); int birakilan_sayisi(void); #endif
// sayac.c — sayaclar yalniz bu birimde tutulur
#include <stdlib.h>
#include "sayac.h"
static int ayrilan = 0;
static int birakilan = 0;
void *say_ayir(size_t boyut) {
void *blok = malloc(boyut);
if (blok != NULL) {
ayrilan++;
}
return blok;
}
void *say_buyut(void *eski, size_t boyut) {
int yeni_blok = (eski == NULL);
void *yeni = realloc(eski, boyut);
if (yeni != NULL && yeni_blok) {
ayrilan++;
}
return yeni;
}
void say_birak(void *blok) {
if (blok != NULL) {
birakilan++;
}
free(blok);
}
int ayrilan_sayisi(void) {
return ayrilan;
}
int birakilan_sayisi(void) {
return birakilan;
}
// duzen.c — dort dil gozlemi ile ayir ve birak karsitligi
#include <stdio.h>
#include <string.h>
#include "sayac.h"
static int sabit_statik = 3;
static int fark(void) {
return ayrilan_sayisi() - birakilan_sayisi();
}
static void gozle(const char *ad, const char *sinif, const char *kanit) {
printf("%-30s%-10s%s\n", ad, sinif, kanit);
}
int main(void) {
char tampon[64];
int once;
printf("%-30s%-10s%s\n", "gozlem", "sahip", "kanit");
/* --- dil: otomatik bolge, kapsam bitince kendiliginden geri alinir --- */
once = fark();
int yerel = 5;
int *p = &yerel;
snprintf(tampon, sizeof tampon, "*p %d, sayac farki %d", *p, fark() - once);
gozle("yerel degisken", "dil", tampon);
/* --- dil: statik bolge, program boyunca tek bir yerde durur --- */
int *s = &sabit_statik;
snprintf(tampon, sizeof tampon, "*s %d, birakma cagrisi yok", *s);
gozle("statik alan", "dil", tampon);
/* --- dil: sabit veri bolgesi, degismez metin --- */
const char *degismez = "sabit";
snprintf(tampon, sizeof tampon, "uzunluk %d, birakma cagrisi yok", (int)strlen(degismez));
gozle("dizgi degismezi", "dil", tampon);
/* --- dil: otomatik bolgede yasayan bir dizi --- */
int dizi[4] = {1, 2, 3, 4};
int *ilk = dizi;
int *son = &dizi[3];
snprintf(tampon, sizeof tampon, "son - ilk %d, *son %d", (int)(son - ilk), *son);
gozle("yerel dizi", "dil", tampon);
/* --- cagiran: obek bolgesi, elle ayrilir ve elle birakilir --- */
once = fark();
int *blok = say_ayir(4 * sizeof *blok);
int toplam = 0;
if (blok != NULL) {
for (int i = 0; i < 4; i++) {
blok[i] = i + 1;
toplam += blok[i];
}
}
say_birak(blok);
snprintf(tampon, sizeof tampon, "toplam %d, sayac farki %d", toplam, fark() - once);
gozle("ayir ve birak", "cagiran", tampon);
return 0;
}
gozlem sahip kanit yerel degisken dil *p 5, sayac farki 0 statik alan dil *s 3, birakma cagrisi yok dizgi degismezi dil uzunluk 5, birakma cagrisi yok yerel dizi dil son - ilk 3, *son 4 ayir ve birak cagiran toplam 10, sayac farki 0
BE16. Dört dil gözleminin dördünde de sayaç hiç kıpırdamadı — yerel degisken ve
yerel dizi otomatik bölgede, statik alan statik bölgede, dizgi degismezi sabit veri
bölgesinde durur; üçü de programın kendisi tarafından, kapsam ya da program ömrüyle
geri alınır. ayir ve birak gözlemi ise öbek bölgesindedir ve sayaç farkı yine 0
verir — ama bu kez sıfır, dilin kendiliğinden yaptığı bir şeyden değil, say_ayir’in
karşılığında yazılan say_birak’tan geliyor. Aynı sonuç (fark 0), iki farklı nedenden
geliyor: biri hiç ölçülmediği için sıfır, öteki ölçülüp dengelendiği için sıfır.
Bölge Sahibi Belirler, Sahip Bölgeyi Belirlemez
Bir bloğun hangi bölgede durduğu, çoğu zaman bildirimin kendisinden bellidir: bir işlev
içinde int yerel = 5; yazmak otomatik bölgeye, dosya kapsamında ya da static anahtar
sözcüğüyle bildirmek statik bölgeye, bir dizgi değişmezi yazmak sabit veri bölgesine, ve
say_ayir (ya da doğrudan malloc) çağırmak öbek bölgesine yerleştirir. Sahiplik sınıfı
bu bildirimden türer: otomatik ve statik bölgedeki hiçbir blok bırakma çağrısı
almaz, sahibi hep dildir; öbek bölgesindeki her blok bir bırakma çağrısı bekler, sahibi
çağırandır (ya da Serbest Bırakma Disiplini dersinde görüldüğü gibi, sözleşmeye göre
çağrılandır). Bölge tek başına yeterli değildir — öbek bölgesindeki bir bloğun
çağırana mı çağrılana mı ait olduğunu bölgenin kendisi söylemez, bunu yine sahip.h gibi
bir sözleşme belirler. Ama otomatik ve statik bölge için bölge tek başına yeterlidir:
oraya yerleşen hiçbir blok hiçbir zaman elle bırakılmaz.
İç İçe Kapsam da Dil Sınıfındadır
Yukarıdaki dört dil gözlemi hepsi main’in en dış kapsamındaydı. Ama otomatik bölgenin
kuralı kapsamın derinliğine bakmaz — bir işlevin gövdesi içindeki her { } bloğu kendi
otomatik ömrünü taşır ve iç içe geçmiş bir blok da dış blok kadar dil sınıfındadır. Aynı
zamanda, dinamik tarafta bir bloğun boyutu büyüdükçe bunun sahiplik sınıfını
değiştirmediğini de göstermek mümkün: ister dört elemanlık ister kırk elemanlık olsun,
say_ayir’den dönen her blok yine çağırana aittir.
// icice.c — ic ice otomatik kapsam ile boyutu buyuyen obek bloginin karsitligi
#include <stdio.h>
#include "sayac.h"
static int fark(void) {
return ayrilan_sayisi() - birakilan_sayisi();
}
static void gozle(const char *ad, const char *sinif, const char *kanit) {
printf("%-30s%-10s%s\n", ad, sinif, kanit);
}
int main(void) {
char tampon[64];
int once;
/* --- dil: ic ice bir blok icindeki otomatik degisken --- */
int disarida = 1;
{
int icerde = 2;
{
int en_icerde = 3;
snprintf(tampon, sizeof tampon,
"disarida %d, icerde %d, en_icerde %d, sayac farki %d",
disarida, icerde, en_icerde, fark());
gozle("uc kat ic ice kapsam", "dil", tampon);
}
}
/* --- cagiran: boyutu on kat buyuyen bir obek blogu yine cagirana ait --- */
once = fark();
enum { KUCUK = 4, BUYUK = 40 };
int *kucuk_blok = say_ayir(KUCUK * sizeof *kucuk_blok);
int *buyuk_blok = say_ayir(BUYUK * sizeof *buyuk_blok);
int toplam = 0;
if (kucuk_blok != NULL && buyuk_blok != NULL) {
for (int i = 0; i < KUCUK; i++) {
toplam += kucuk_blok[i] = i;
}
for (int i = 0; i < BUYUK; i++) {
toplam += buyuk_blok[i] = i;
}
}
say_birak(kucuk_blok);
say_birak(buyuk_blok);
snprintf(tampon, sizeof tampon, "toplam %d, sayac farki %d", toplam, fark() - once);
gozle("kucuk ve buyuk blok", "cagiran", tampon);
return 0;
}
uc kat ic ice kapsam dil disarida 1, icerde 2, en_icerde 3, sayac farki 0 kucuk ve buyuk blok cagiran toplam 786, sayac farki 0
BE17. uc kat ic ice kapsam gözleminde üç değişken üç ayrı { } derinliğinde
bildirildi, ama üçü de otomatik bölgede ve üçü de sayacı hiç etkilemedi — kapsamın
derinliği, dil sınıfının geçerliliğini değiştirmiyor. kucuk ve buyuk blok gözlemi ise
boyutu on kat farklı iki öbek bloğunu (4 ve 40 eleman) yan yana ayırıp bırakıyor; sayaç
farkı yine 0, ama bu kez iki ayrı say_ayir/say_birak çiftinin dengelenmesinden.
Bu, Bölge Sahibi Belirler bölümündeki iddiayı derinliğe ve boyuta karşı sınıyor: bir
bloğun otomatik mi öbek mi olduğunu belirleyen ne kapsamın kaç kat iç içe olduğu ne de
bloğun kaç bayt tuttuğu — belirleyen yalnız hangi bildirimle var olduğudur. int en_icerde üç kapsam derinliğinde de olsa dil sınıfındandır; say_ayir(40 * sizeof(int))
tek bir çağrıda da olsa çağıran sınıfındandır.
Aynı Sınıf, Farklı Kalıcılık
dil sınıfındaki hiçbir blok elle bırakılmaz — ama bu, dil sınıfının içindeki her
bölgenin aynı davrandığı anlamına gelmez. Otomatik bölge her çağrıda sıfırdan başlar,
statik bölge çağrılar arasında kalıcıdır. İkisi de sahiplik sorusuna aynı yanıtı verir
(sahip dil, bırakma çağrısı yok), ama ikisinin ömrü birbirinden tamamen farklıdır.
// kalici.c — islev icindeki static degiskenin cagrilar arasinda kalici olmasi
#include <stdio.h>
#include "sayac.h"
static int fark(void) {
return ayrilan_sayisi() - birakilan_sayisi();
}
static int sayac_tut(void) {
static int cagri = 0;
cagri++;
return cagri;
}
static int yerel_sifirlanir(void) {
int deger = 0;
deger++;
return deger;
}
int main(void) {
char tampon[96];
int once = fark();
int birinci = sayac_tut();
int ikinci = sayac_tut();
int ucuncu = sayac_tut();
int otomatik_birinci = yerel_sifirlanir();
int otomatik_ikinci = yerel_sifirlanir();
snprintf(tampon, sizeof tampon,
"static cagrilar %d %d %d, otomatik cagrilar %d %d, sayac farki %d",
birinci, ikinci, ucuncu, otomatik_birinci, otomatik_ikinci, fark() - once);
printf("%-30s%s\n", "kalici ile sifirlanan", tampon);
return 0;
}
kalici ile sifirlanan static cagrilar 1 2 3, otomatik cagrilar 1 1, sayac farki 0
BE18. sayac_tut içindeki cagri değişkeni static bildirildiği için statik bölgede
durur ve üç çağrı boyunca aynı değeri taşır: 1, 2, 3. yerel_sifirlanir içindeki
deger ise otomatik bölgede, her çağrıda yeniden bildirilir ve 0’dan başlar; iki çağrının
ikisi de 1 döndürüyor, çünkü ikinci çağrı birincinin bıraktığı yerden devam etmiyor.
Sayaç farkı hâlâ 0, çünkü hiçbir öbek ayırması yapılmadı — bu ölçüm sahiplik sınıfını
değil, aynı sınıfın içindeki ömür farkını gösteriyor. Bölge Sahibi Belirler, Sahip Bölgeyi Belirlemez bölümündeki kural burada da geçerli: static anahtar sözcüğü bir
değişkeni otomatik bölgeden statik bölgeye taşır ve bu taşıma, hâlâ hiçbir bırakma çağrısı
gerektirmeyen dil sınıfının içinde kalır — ama kalıcılığı kökten değiştirir.
Sınırlayıcı Ölçüm
Bölgeler arasındaki sınırların nerede durduğu ve bir bloğun hangi hizalama değerine göre
yerleştiği çalışma zamanı ortamına bağlıdır; bu ders ne bir adres ne bir hizalama değeri
basıyor. son - ilk farkı 3 verdi — bu, iki adresin kendisi değil, aralarındaki
eleman sayısıdır ve bölge sınırından bağımsız, taşınabilir bir ölçüdür.
Otomatik bölgenin boyutu da sınırsız değildir — her çağrı çerçevesi, o bölgede sınırlı bir
yer kaplar ve iç içe kapsamlar (uc kat ic ice kapsam gözlemindeki gibi) ya da derin bir
özyineleme bu yeri tüketebilir. Bu sınırın aşılması tanımsız davranıştır (yaygın adıyla
yığıt taşması) ve bu ders, kursun geri kalanındaki tanımsız davranış kuralına uyarak bunu hiç
koşturmuyor — yalnız var olduğunu belirtiyor. Öbek bölgesinin sınırı ise farklı bir
biçimde görünür: say_ayir boyutu karşılayamadığında sessizce başarısız olmaz,
malloc’ın sözleşmesine uygun biçimde NULL döndürür ve bu ders boyunca her ayırma bu
dönüşle kontrol edildi (kucuk_blok != NULL && buyuk_blok != NULL). Otomatik bölgenin
taşması bir çökmeyle, öbek bölgesinin dolması bir NULL dönüşüyle kendini gösterir —
ikisi de bu bölgelerin sınırsız olmadığının kanıtı, ama biri denetlenebilir bir dönüş
değeri üretir, öteki üretmez.
Özet
- Bellek dört bölgeye ayrılır: otomatik, statik, sabit veri ve öbek; bu dersin ölçtüğü bellek sınıfı dildir.
- Otomatik ve statik bölgedeki bloklar hiçbir zaman elle bırakılmaz — sahibi dil, sayaç hiç kıpırdamaz.
- Öbek bölgesindeki bloklar
say_ayir/say_birakile dengelenir; sayaç farkı yine 0 çıkabilir ama bu kez ölçülüp dengelendiği için. - Bölge, otomatik ve statik bellek için sahiplik sınıfını tek başına belirler; öbek bölgesinde sınıfı belirleyen bölge değil, sözleşmedir.
- Bölge sınırları ve hizalama değerleri ortama bağlıdır; bu derste basılmadı, M01/K01’e bırakıldı.
Sonraki Adım
Dinamik Bellek konusu burada kapanıyor: dört sahiplik sınıfının (dil, çağıran, çağrılan, sahipsiz) her biri en az bir ölçümle görüldü. Sıradaki konu, Sağlamlık, sözleşmenin kırıldığı anı konu alıyor — bir kural yazılı olduğu hâlde bilerek çiğnendiğinde ne olduğunu, hangi durumların yalnız düzyazıda tarif edilip hiç koşturulmadığını ve yanlarına hangi tanımlı karşılığın koşturulduğunu.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.