---
title: 'Sınır Denetimi'
source: 'https://academia.sh/tr/kurslar/c-bellek/sinir-denetimi'
course: "C'de Bellek ve İşaretçiler"
language: tr
updated: '2026-09-26T03:02:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "kayit_olustur'un sinir parametresini çağıranın verdiği bir sözleşme olarak tanır"
    - 'Uzunluğu bilinmeyen bir kaynağı sabit boyutlu bir arabelleğe snprintf ile kesme bildirimiyle kopyalar'
    - 'Sınırın kaynakta değil, çağıranda yazılı olduğunu iki farklı sınır değeriyle ölçer'
---

# Sınır Denetimi

kayit_olustur'un sinir parametresi uzunluğu kaynaktan değil, çağırandan alır; ders bu parametrenin bir arabellek taşmasını nasıl önlediğini, snprintf'in kesme bildirimini ve uzunluğun kimin sorumluluğunda olduğunu koşturarak gösterir.

Önceki ders `kayit_olustur`'un hata yolunda temizliğini ölçtü; o hata yolu bir tek koşuldan
doğuyordu: verilen adın uzunluğu `sinir` parametresini aşıyordu. Bu ders o parametrenin
kendisini merkeze alıyor. `sinir`, dilin ya da `sahip.c` biriminin bir sabiti değildir —
**çağıranın** her çağrıda verdiği bir değerdir ve bir arabellek taşmasını önleyen şey tam
olarak budur.

Önceki iki ders "sahipsiz" ve "çağrılan" sınıflarını ölçtü; bu ders üçüncü sınıfa geçiyor:
**çağıran**. Aradaki fark şu soruda somutlaşıyor — bir arabellek taşmasını kim önler? Önceki
derste yanıt "çağrılan işlevin kendisi"ydi: `kayit_olustur` kendi ayırdığı bloğu kendi
sınırlıyordu. Bu derste ölçülen iki işlev de (`kayit_olustur`'un `sinir`'i ve
`sinirli_kopyala`'nın `hedef_boyut`'u) sınırı **kendileri belirlemiyor** — sınırı çağıran
belirliyor ve işlev yalnız o sınıra **uyuyor**. Sınırın doğru olup olmadığı, işlevin değil,
çağıranın sorumluluğudur.

## Uzunluğu Çağıran Verir

`kayit_olustur(const char *ad, size_t sinir)` imzasına bakıldığında `sinir`'in nereden
geldiği görünmez; imza yalnız bir `size_t` alır. Ama sözleşme başlık dosyasındaki yorumda
yazılıdır: işlev, `ad`'in uzunluğunu `sinir`'e göre denetler ve sığmazsa `NULL` döner. Bu
denetim dilin bir parçası değildir — `strlen` sınır tanımaz, `memcpy` sınır tanımaz; sınırı
tanıyan tek şey `kayit_olustur`'un kendi gövdesindeki karşılaştırmadır.

Bu noktada dikkat edilmesi gereken şey, `sinir`'in **nereden** geldiğidir. İmzada `size_t
sinir` yazması, değerin sabit ya da güvenli olduğu anlamına gelmez — çağıran onu bir
sabitten, bir kullanıcı girdisinden ya da başka bir hesaplamadan türetebilir. `kayit_olustur`
kendisine verilen `sinir`'in **doğru** olup olmadığını denetlemez, yalnız `ad`'in o `sinir`'e
sığıp sığmadığını denetler. Sınırın kendisi yanlışsa — örneğin gerçek hedef kapasitesinden
büyükse — işlev hâlâ doğru çalışır ama denetimin koruduğu şey artık gerçek hedef değil,
çağıranın yanlış bildirdiği sayıdır. Bu ayrım dersin sonundaki "Sınırlayıcı Ölçüm" bölümünde
yeniden ele alınıyor.

```c
// 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
```

```c
// 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; }
```

```c
// sahip.h — kaydin ici gizli, sahiplik sozlesmesi yorumlarda yazili
#ifndef SAHIP_H
#define SAHIP_H

#include <stddef.h>

typedef struct Kayit Kayit;

/* Cagiran sahiplenir ve kayit_yok_et ile birakir. Ad sinira sigmazsa NULL
   doner; o ana kadar ayrilan bellegi islev kendisi birakir. */
Kayit *kayit_olustur(const char *ad, size_t sinir);

/* Kaydi ve kaydin icindeki ad tamponunu birlikte birakir. */
void kayit_yok_et(Kayit *k);

/* Kayit sahiplenir: donen isaretci kayit yok edilene dek gecerlidir,
   cagiran onu birakmaz. */
const char *kayit_ad(const Kayit *k);

/* Cagiran sahiplenir: donen kopya say_birak ile birakilir. */
char *kayit_ad_kopyasi(const Kayit *k);

#endif
```

```c
// sahip.c — sozlesmenin uygulandigi birim
#include <string.h>
#include "sahip.h"
#include "sayac.h"

struct Kayit {
    char *ad;
    size_t uzunluk;
};

Kayit *kayit_olustur(const char *ad, size_t sinir) {
    Kayit *k = say_ayir(sizeof *k);
    if (k == NULL) { return NULL; }

    k->uzunluk = strlen(ad);
    if (k->uzunluk >= sinir) {
        say_birak(k);
        return NULL;
    }

    k->ad = say_ayir(k->uzunluk + 1);
    if (k->ad == NULL) {
        say_birak(k);
        return NULL;
    }
    memcpy(k->ad, ad, k->uzunluk + 1);
    return k;
}

void kayit_yok_et(Kayit *k) {
    if (k == NULL) { return; }
    say_birak(k->ad);
    say_birak(k);
}

const char *kayit_ad(const Kayit *k) { return k->ad; }

char *kayit_ad_kopyasi(const Kayit *k) {
    char *kopya = say_ayir(k->uzunluk + 1);
    if (kopya != NULL) { memcpy(kopya, k->ad, k->uzunluk + 1); }
    return kopya;
}
```

**SL7.** `kayit_olustur` aynı `ad` ile iki farklı `sinir` değeriyle çağrıldığında iki farklı
sonuç verebilir — sonucu belirleyen `ad` değil, çağıranın verdiği `sinir`'dir. Aşağıdaki ölçüm
bunu tam olarak bu şekilde gösteriyor: aynı ad, biri dar biri geniş iki sınırla.

Bu gözlem, önceki dersteki `kayit_olustur` ölçümünden kasıtlı olarak farklı bir açıdan
bakıyor. Önceki ders `sinir`'i **sabit tuttu** ve `ad`'in uzunluğunu değiştirerek hata yolunu
tetikledi; bu ders tam tersini yapıyor — `ad`'i **sabit tutuyor** ve `sinir`'i değiştiriyor.
İkisi de aynı karşılaştırmayı (`k->uzunluk >= sinir`) tetikliyor, ama hangi tarafın
değiştiğini göstermek, sözleşmenin hangi parametreye ait olduğunu netleştiriyor: karşılaştırma
her iki değişkene de bakıyor, ama bu derste sorulan soru özellikle "sınırı kim veriyor"
olduğu için, sabit tutulması gereken taraf `ad`, değiştirilmesi gereken taraf `sinir`'dir.

## Sabit Boyutlu Arabelleğe Kesmeli Kopyalama

`kayit_olustur` kaynağı ayırıp kopyalıyor; ama bazen hedef **sabit boyutlu** bir yerel
arabellektir ve kaynağın uzunluğu önceden bilinmez. Bu durumda `strcpy` kullanmak
arabellek taşmasına açık kapı bırakır: `strcpy` hedefin boyutunu hiç bilmez, kaynağı
olduğu gibi kopyalar. `snprintf` ise hedefin boyutunu bir parametre olarak alır ve asla
o boyutu aşmaz; kestiğinde de bunu dönüş değeriyle **bildirir** — dönüş değeri, kesme
olmasaydı yazılacak olan toplam uzunluktur.

Bu iki kalıp arasındaki fark, önceki bölümdeki `kayit_olustur`'la karşılaştırıldığında daha
net görülüyor. `kayit_olustur` sınırı aştığında hiçbir şey yazmadan `NULL` döner — kısmi bir
yazma söz konusu değildir. `sinirli_kopyala` ise farklı davranır: hedefe **sığdığı kadarını**
yazar ve gerçek uzunluğu ayrıca bildirir. İkisi de arabellek taşmasını önlüyor, ama biri
"ya tam sığar ya da hiç yazmam" derken öbürü "sığdığım kadarını yazarım, ama sana kaç
karakterin sığmadığını da söylerim" diyor. Hangi kalıbın uygun olduğu, hedefin ne için
kullanılacağına bağlıdır: bir kayıt adı gibi bütünlüğü bozulmaması gereken bir veri için
ilki, bir günlük satırı gibi kesilmesi kabul edilebilir bir metin için ikincisi tercih
edilir.

```c
// kesme.h — sinirli hedefe kesmeli kopyalamanin bildirimi
#ifndef KESME_H
#define KESME_H

#include <stddef.h>

/* Hedefin boyutu hedef_boyut'tur. Donen deger, kesme olmasaydi yazilacak
   toplam uzunluktur; bu deger hedef_boyut'tan buyukse kesme olmustur. */
size_t sinirli_kopyala(char *hedef, size_t hedef_boyut, const char *kaynak);

#endif
```

```c
// kesme.c — snprintf ile sinir asilmadan kopyalama
#include <stdio.h>
#include "kesme.h"

size_t sinirli_kopyala(char *hedef, size_t hedef_boyut, const char *kaynak) {
    int yazilacak = snprintf(hedef, hedef_boyut, "%s", kaynak);
    if (yazilacak < 0) {
        return 0;
    }
    return (size_t)yazilacak;
}
```

**SL8.** `sinirli_kopyala`'nın döndürdüğü değer ile `hedef_boyut - 1` karşılaştırıldığında
kesilip kesilmediği anlaşılır: dönüş değeri hedefe **sığmayan** uzunluktur, hedefe fiilen
yazılan uzunluk değildir. Bu iki sayı aynı olabilir (kesme yoksa) ya da farklı olabilir
(kesme varsa) — ders aşağıdaki ölçümde ikisini de gösteriyor. Karşılaştırmanın `hedef_boyut -
1` ile yapılması, `snprintf`'in hedefe her zaman bir sonlandırıcı sıfır bayt eklediğinin
sonucudur: `hedef_boyut` baytlık bir alanda en fazla `hedef_boyut - 1` karakter ve bir
sonlandırıcı sığar.

## İki Sınırla Tek Ölçüm

Aşağıdaki tek `main`, aynı iki kaynağı iki farklı sınırla ölçüyor: `kayit_olustur` için dar
ve geniş bir `sinir`, `sinirli_kopyala` için küçük ve yeterli bir `hedef_boyut`.

```c
// olcum.c — cagiranin verdigi sinirin iki farkli degerle olculmesi
#include <stdio.h>
#include "sahip.h"
#include "sayac.h"
#include "kesme.h"

static void gozle(const char *ad, const char *sonuc) {
    printf("%-28s%s\n", ad, sonuc);
}

int main(void) {
    char tampon[64];

    printf("%-28s%s\n", "gozlem", "sonuc");

    /* --- kayit_olustur: ayni ad, iki farkli sinir --- */
    Kayit *dar = kayit_olustur("saglamlik", 6);
    snprintf(tampon, sizeof tampon, "kayit %s", dar == NULL ? "reddedildi" : "kabul edildi");
    gozle("dar sinir (6)", tampon);

    Kayit *genis = kayit_olustur("saglamlik", 32);
    snprintf(tampon, sizeof tampon, "kayit %s", genis == NULL ? "reddedildi" : "kabul edildi");
    gozle("genis sinir (32)", tampon);
    kayit_yok_et(genis);

    /* --- sinirli_kopyala: ayni kaynak, iki farkli hedef boyutu --- */
    char kucuk[8];
    size_t gerekli_kucuk = sinirli_kopyala(kucuk, sizeof kucuk, "saglamlik dersi");
    snprintf(tampon, sizeof tampon, "gerekli %zu, sigan %zu, %s",
             gerekli_kucuk, sizeof kucuk - 1,
             gerekli_kucuk >= sizeof kucuk ? "kesildi" : "kesilmedi");
    gozle("kucuk hedef (8)", tampon);

    char yeterli[32];
    size_t gerekli_yeterli = sinirli_kopyala(yeterli, sizeof yeterli, "saglamlik dersi");
    snprintf(tampon, sizeof tampon, "gerekli %zu, sigan %zu, %s",
             gerekli_yeterli, sizeof yeterli - 1,
             gerekli_yeterli >= sizeof yeterli ? "kesildi" : "kesilmedi");
    gozle("yeterli hedef (32)", tampon);

    return 0;
}
```

```text
gozlem                      sonuc
dar sinir (6)               kayit reddedildi
genis sinir (32)            kayit kabul edildi
kucuk hedef (8)             gerekli 15, sigan 7, kesildi
yeterli hedef (32)          gerekli 15, sigan 31, kesilmedi
```

**SL9.** `"saglamlik"` dokuz karakterdir; dar sınır (**6**) onu reddediyor, geniş sınır
(**32**) aynı adı kabul ediyor — aradaki fark kaynakta değil, çağıranın verdiği sayıdadır.
`sinirli_kopyala`'da da aynı kaynak (`"saglamlik dersi"`, on beş karakter) iki farklı hedef
boyutuyla ölçülüyor: sekiz baytlık hedef yedi karaktere sığdığı için kesiliyor, otuz iki
baytlık hedef on beş karaktere rahatça sığdığı için kesilmiyor — `gerekli` değeri her iki
çağrıda da **aynı** kalıyor, değişen yalnız hedefin kapasitesi.

Dört gözlemin ikisi `kayit_olustur` üzerinden, ikisi `sinirli_kopyala` üzerinden geliyor, ama
dördü de aynı yapıyı paylaşıyor: **aynı kaynak, iki farklı sınır**. Bu tekrar kasıtlı — dersin
tek bir kanıtı yok, iki bağımsız işlevde aynı ilkeyi gösteren iki kanıtı var. `kayit_olustur`
sınırı aşınca tamamen reddediyor; `sinirli_kopyala` sınırı aşınca kısmen kabul edip durumu
bildiriyor. İki farklı tepki, aynı kaynağın (çağıranın verdiği sınır) iki farklı sonucu
nasıl belirleyebileceğini gösteriyor. `main` içindeki `once`/`fark` düzenine burada ihtiyaç
yok, çünkü hiçbir gözlem bir öncekinin sayaç durumuna bağlı değil — her ikisi de kendi
girdisiyle, kendi sınırıyla bağımsız ölçülüyor; `dar` ve `genis` işaretçilerinden yalnız
`genis` bırakılıyor çünkü `dar` zaten `NULL`, `kayit_yok_et(NULL)` de güvenle hiçbir şey
yapmıyor.

## Sınırlayıcı Ölçüm

`sinir` ve `hedef_boyut` parametrelerinin **doğru** değerle çağrılması derleyicinin
denetlediği bir şey değildir. `kayit_olustur(ad, 1000000)` gibi gerçek boyuttan kopuk, aşırı
büyük bir sınır vermek de derlenir; bu durumda işlev kendi içindeki denetimi hâlâ doğru
uygular, ama denetim artık **anlamsız** hale gelir çünkü çağıran gerçek hedefin boyutunu
yanlış bildirmiştir. Sınır denetimi, sınırın kendisi yanlışsa hiçbir şeyi kurtarmaz.

Aynı sınır, `sinirli_kopyala`'nın `hedef_boyut` parametresi için de geçerlidir. `sizeof kucuk`
ifadesi burada doğru çalışıyor çünkü `kucuk` gerçek bir dizi değişkenidir ve derleyici onun
boyutunu bilir; ama bir işaretçi parametresine `sizeof` uygulansaydı — örneğin `kucuk` bir
işlev parametresi olarak `char *kucuk` şeklinde alınsaydı — `sizeof kucuk` dizinin boyutunu
değil, işaretçinin kendi boyutunu verirdi ve bu da sessizce yanlış bir `hedef_boyut`
üretirdi. Bu derste `sizeof kucuk` ve `sizeof yeterli` doğrudan `main` içindeki dizi
tanımlarının üzerinde çağrıldığı için bu tuzağa girilmiyor, ama kalıbı başka bir işleve
taşırken bu ayrım gözden kaçırılabilir; `hedef_boyut`'u her zaman **dizinin tanımlandığı
kapsamda** hesaplamak, işaretçiye dönüştükten sonra hesaplamamak gerekir.

Sınırın doğruluğu bir kez saptandıktan sonra bile, sınırın **kalıcılığı** ayrı bir sorudur.
`kayit_olustur`'a verilen `sinir` yalnız o tek çağrı için geçerlidir; aynı `Kayit` daha sonra
başka bir işleve geçirilirken orijinal `sinir` değeri taşınmaz — yalnız `kayit_ad`'in
döndürdüğü işaretçi ve `kayit_ad_kopyasi`'nin ürettiği kopya taşınır. Bu, çağıranın
sorumluluğunun tek bir çağrıyla bitmediğini gösteriyor: sınırı ilk veren çağıran, o sınırın
hangi varsayımla (örneğin "bu ad en fazla otuz bir karakter olacak") verildiğini de
belgelemek zorundadır, çünkü sonraki kod o varsayımı kaynaktan okuyarak değil, yalnız
belgeden okuyarak öğrenebilir.

## Özet

- `kayit_olustur`'un `sinir` parametresi kaynaktan değil **çağırandan** gelir; aynı ad, iki
  farklı sınırla iki farklı sonuç verdi.
- `snprintf`, hedefin boyutunu parametre olarak alıp asla aşmayan ve kesildiğinde bunu dönüş
  değeriyle bildiren bir kopyalama sağladı; `strcpy` bu bildirimi hiç yapmaz.
- İki ayrı hedef boyutuyla aynı kaynak ölçüldüğünde `gerekli` uzunluk sabit kaldı, yalnız
  kesilip kesilmediği değişti — sınırın doğruluğu hedefin gerçek kapasitesine bağlıdır.
- Bu dersin ölçtüğü bellek **çağıran** sınıfındadır: hedefin boyutunu bilen ve bildiren taraf
  koddaki `sinir` ya da `hedef_boyut` parametresini veren çağrının kendisidir.
- Bu üç ders, kursun "kim sahipleniyor" ekseninde üç ayrı sınıfı sırayla ölçtü: önceki iki
  ders sahipsiz ve çağrılan sınıflarını sözleşmenin kırıldığı ve işlevin başarısız olduğu
  anlarla, bu ders çağıran sınıfını sınırın kaynakta değil çağıranda yazılı olduğunu
  göstererek ölçtü. Üç dersin ortak paydası, hiçbirinde koruyucu tarafın dil olmamasıdır —
  koruma her seferinde ayrı bir tarafın kendi eklediği bir satırdan geliyor.

## Sonraki Adım

Bu üç ders sözleşmenin kırıldığı, işlevin başarısız olduğu ve çağıranın sınır verdiği anları
ölçtü — üçü de aynı platformda, aynı derleyicide koşturuldu. Bu ortak varsayım şimdiye kadar
hiç sorgulanmadı: her ölçüm, aynı `cc`, aynı bayraklar, aynı işletim sistemi altında aynı
sonucu üretti. Ama tanımlı davranışın kendisi bile her yerde birebir aynı değildir — bir
kısmı standarda göre tanımlıyken, hangi **değeri** alacağı uygulamaya bağlı kalabilir; bu
derste ölçülen `sinirli_kopyala`'nın döndürdüğü uzunluk türü olan `size_t`'in kendi genişliği
bile platformdan platforma değişebilir. Sonraki ders bu sözleşmenin **platformlar arasında**
ne kadar değiştiğini ele alıyor: hangi davranış tanımlı kalır, hangisi uygulamaya bağlıdır ve
kod bunu nasıl taşınabilir tutar. Bu dersteki üç örnek de bu yüzden yalnız bir platformun
kanıtıdır — sonucun kendisi değişmeden kalsa bile, hangi varsayımların o sonucu taşıdığı
henüz açık yazılmadı. O yazım işi, ders kodunun bir satırını bile değiştirmeden, yalnız
derleme ortamını değiştirerek başlıyor.
