---
title: 'C Temelleri'
source: 'https://academia.sh/tr/kurslar/c-temelleri'
language: tr
updated: '2026-09-13T05:07:32+00:00'
license: 'CC BY-SA 4.0'
lessons: 13
outcomes:
    - 'Dört derleme aşamasını ayırt eder ve bir kararın hangisinde verildiğini ölçer'
    - 'Başlık dosyaları ile ön işlemci yönergelerini yerinde kullanır'
    - 'Temel tipler, işleçler ve denetim akışıyla program yazar'
    - 'Diziler, dizgiler, yapılar ve girdi çıktı düzeneklerini birlikte kullanır'
---

# C Temelleri

C'de bir kaynak tek adımda değil dört adımda programa dönüşür ve bir kararın nerede verildiği bunu bilmeden okunamaz: kurs her düzeneği tek bir soruyla ölçer — bu karar ön işlemcide mi, derleyicide mi, bağlayıcıda mı, yoksa koşumda mı veriliyor? Derleme aşamaları, başlık dosyaları, temel tipler, işleçler, fonksiyonlar, diziler ve girdi çıktı aynı çerçeveden geçirilir.

## Dil ve Derleme

Dilin donanıma yakınlığının hangi tasarım kararlarından doğduğu, bir kaynağın programa dönüşürken geçtiği dört aşamanın ayrı ayrı gözlenmesi, bildirimlerin başlık dosyalarıyla paylaşılması, metin düzeyinde çalışan ön işlemcinin tip bilmemesi, ve derleyici uyarılarının hangi standarda göre doğduğu.

### C'nin Konumu

Source: https://academia.sh/tr/kurslar/c-temelleri/cnin-konumu

C'de bir kaynağın çalışan bir programa dönüşmesi tek adım değildir: dört aşamadan geçer ve her aşama ayrı bir soruya bakar. Üç dosyalık bir ölçüm çekirdeği bu dört aşamayı tek koşumda gösterir ve on dört gözlemin 4'ünü ön işlemciye, 4'ünü derleyiciye, 3'ünü bağlayıcıya, 3'ünü koşuma yazar.

Rust müfredatı beş kursla kapandı ve son kursu tek bir soru sordu: derleyicinin verdiği
güvence nerede bitiyor (M10/K05). C'de bu sorudan önce gelen bir soru var. Bir kaynak dosya
çalışan bir programa dönüşene kadar kaç kez el değiştiriyor? Yanıt tek bir adım değil,
dörttür — ve bu kurs boyunca sorulacak tek soru, bir kararın hangi elde verildiğidir.

Rust'ta bu tür bir soru tek bir yerde sorulmuştu: kaynakta yazılan bir satır bir değer mi
üretiyor (M10/K01). C'de aynı tür soru dört kez sorulur, çünkü kaynak programa dönüşene kadar
dört ayrı eli geçiyor ve her el kendi kararını kendi bildiği şeye bakarak veriyor. Bu ders o
dört eli adlandırıyor ve aralarındaki farkı tek bir programı derleyip çalıştırarak gösteriyor.

#### Dört Aşama

Bir C kaynağı programa dönüşürken sırayla dört elden geçer. Kurs boyunca bu dört ad değişmez:

| Sınıf | Tanım |
|---|---|
| ön işlemci (preprocessor) | Derleyici kaynağı görmeden önce, metin düzeyinde karara bağlanır |
| derleyici (compiler) | Tek bir birim çevrilirken, tiplerden ve sabitlerden karara bağlanır |
| bağlayıcı (linker) | Birimler bir araya gelirken, hangi tanımın kullanılacağı belli olur |
| koşum (runtime) | Ancak program çalışırken, veriye bakılarak belli olur |

Bu dört adın ortak noktası, hepsinin aynı soruya farklı bir yanıt vermesidir: "bu kararı
vermek için hangi bilgiye ihtiyaç var?" Ön işlemci yalnız metne bakar, tip diye bir şey
bilmez. Derleyici tek bir çeviri birimini (translation unit) görür, ama o birimin dışındaki
hiçbir şeyi bilmez. Bağlayıcı birden çok birimi bir araya getirir, ama hiçbirinin içeriğini
yorumlamaz — yalnız adları eşler. Koşum ise yalnız veriye bakar; kaynağın kendisi artık
elinde bile değildir.

Bu ölçü, M08/K05'teki "bir tipin hangi sınıf yolunda bulunması gerektiği" sorusuyla ve
M09/K05'teki "bir bilginin kaynağının ne olduğu" (kaynak, araç, koşum) sorusuyla karışmaz.
Orada ölçülen bir bağımlılığın nerede arandığı ya da bir bilginin kimden geldiğiydi; burada
ölçülen bir kararın hangi aşamada verildiği ve aşama sayısının dört olduğudur.

#### Aşamalar Neden Görünür

Bu dört aşamanın görünür olması dilin donanıma yakınlığının bir sonucudur. C, kaynaktan
çalışan bir ikiliye giden yolun hiçbir adımını programcıdan gizlemez: ön işlemcinin ürettiği
metin okunabilir bir ara çıktıdır, derleyicinin ürettiği nesne dosyası incelenebilir,
bağlayıcının hangi tanımı seçtiği izlenebilir. Bu görünürlük bir eksiklik değil, dilin kendi
tasarım kararıdır — ve kursun geri kalanında tekrar tekrar karşımıza çıkacaktır.

C standardı (C17) kaynaktan ikiliye giden yolu sekiz çeviri aşamasına (translation phase)
ayırır. Bu kurs o sekiz aşamayı üç sınıfta toplar — ön işlemci, derleyici, bağlayıcı — ve
üstüne dördüncü sınıf olarak **koşumu** ekler; koşum standardın çeviri aşamalarına dahil
değildir, ayrı bir maddede yürütme ortamı olarak tanımlanır. Programcının kendi verdiği
kararlar bu dört noktada toplanır. Standardın sekiz aşaması arasındaki ayrımlar — örneğin
metin kodlamasının çözülmesiyle satır sonlarının birleştirilmesi ayrı aşamalardır — bu kursun
ölçtüğü kararları
etkilemez; onlar için tek isim yeter: ön işlemci.

#### Ölçüm Çekirdeği

Kursun geri kalanında kullanılacak çekirdek üç dosyadır. Bağlayıcı sınıfını ölçmek birden
fazla birim gerektirdiği için çekirdek baştan çok birimlidir: bir başlık dosyası, iki kaynak
dosyası. İlk dosya yalnız bildirim taşır — hiçbir tanım içermez.

```c
// asama.h — baska birimlerde tanimli adlarin bildirimi
#ifndef ASAMA_H
#define ASAMA_H

/* Bu iki islev baska bir birimde tanimli: cagrilari baglama asamasinda cozulur. */
int kimlik(void);
const char *birim_adi(void);

/* Bu degisken de baska bir birimde tanimli. */
extern int birim_sayaci;

#endif
```

İkinci dosya bu bildirimlerin karşılığını taşıyor. `secim.c` kendi başına derlenebilir bir
birimdir ve `asama.c`'nin hiçbir satırını bilmez.

```c
// secim.c — ikinci birim: tanimlar burada, cagrilar baglamada cozulur
#include "asama.h"

int birim_sayaci = 2;

int kimlik(void) {
    return 7;
}

const char *birim_adi(void) {
    return "secim";
}
```

Üçüncü dosya asıl ölçümü taşıyor. On dört ayrı gözlemi bir tabloya yazıyor, her satırın
hangi aşamada karara bağlandığını da yanına ekliyor.

```c
// asama.c — bir kararin hangi asamada verildigini olcen cekirdek
#include <stdio.h>
#include <string.h>
#include "asama.h"

#define KAT 3
#define BIRLESTIR(a, b) a##b

enum Renk { KIRMIZI = 1, YESIL = 2, MAVI = 4 };

static int BIRLESTIR(topla, _uc)(int x) {
    return x + 3;
}

static void gozle(const char *ad, const char *sinif, const char *kanit) {
    printf("%-30s%-14s%s\n", ad, sinif, kanit);
}

int main(void) {
    char tampon[64];
    int on_islemci = 0, derleyici = 0, baglayici = 0, kosum = 0;

    printf("%-30s%-14s%s\n", "gozlem", "asama", "kanit");

    /* --- on islemci --- */

    snprintf(tampon, sizeof tampon, "KAT metni %d olarak konuldu", KAT);
    gozle("makro degeri", "on islemci", tampon);
    on_islemci++;

#ifdef AYRINTILI
    gozle("kosullu govde", "on islemci", "ayrintili govde derlemeye girdi");
#else
    gozle("kosullu govde", "on islemci", "kisa govde derlemeye girdi");
#endif
    on_islemci++;

    snprintf(tampon, sizeof tampon, "uretilen ad %d verdi", BIRLESTIR(topla, _uc)(4));
    gozle("ad birlestirme", "on islemci", tampon);
    on_islemci++;

    gozle("baslik metni", "on islemci", "bildirimler kaynaga metin olarak kopyalandi");
    on_islemci++;

    /* --- derleyici --- */

    snprintf(tampon, sizeof tampon, "sizeof(char) == %d", (int)sizeof(char));
    gozle("tip boyutu birimi", "derleyici", tampon);
    derleyici++;

    int sabit_dizi[2 * 3 + 1];
    snprintf(tampon, sizeof tampon, "eleman sayisi %d", (int)(sizeof sabit_dizi / sizeof sabit_dizi[0]));
    gozle("sabit ifade katlamasi", "derleyici", tampon);
    derleyici++;

    snprintf(tampon, sizeof tampon, "MAVI sabiti %d", (int)MAVI);
    gozle("numaralandirma sabiti", "derleyici", tampon);
    derleyici++;

    int tam = 7 / 2;
    double kesirli = 7 / 2.0;
    snprintf(tampon, sizeof tampon, "tam %d kesirli %.1f", tam, kesirli);
    gozle("islecin tipe gore secimi", "derleyici", tampon);
    derleyici++;

    /* --- baglayici --- */

    snprintf(tampon, sizeof tampon, "cagri %d verdi", kimlik());
    gozle("baska birimdeki islev", "baglayici", tampon);
    baglayici++;

    snprintf(tampon, sizeof tampon, "birim %s olarak bildirildi", birim_adi());
    gozle("baska birimdeki ad", "baglayici", tampon);
    baglayici++;

    snprintf(tampon, sizeof tampon, "sayac %d", birim_sayaci);
    gozle("baska birimdeki degisken", "baglayici", tampon);
    baglayici++;

    /* --- kosum --- */

    int veri[] = {4, 8, 15, 16};
    int toplam = 0;
    for (size_t i = 0; i < sizeof veri / sizeof veri[0]; i++) {
        toplam += veri[i];
    }
    snprintf(tampon, sizeof tampon, "toplam %d", toplam);
    gozle("veriden hesaplanan deger", "kosum", tampon);
    kosum++;

    const char *metin = "merhaba dunya";
    snprintf(tampon, sizeof tampon, "uzunluk %d", (int)strlen(metin));
    gozle("dizgi uzunlugu", "kosum", tampon);
    kosum++;

    const char *karar = (toplam > 40) ? "buyuk" : "kucuk";
    gozle("veriye bagli dallanma", "kosum", karar);
    kosum++;

    printf("\n%-14s%d\n", "on islemci", on_islemci);
    printf("%-14s%d\n", "derleyici", derleyici);
    printf("%-14s%d\n", "baglayici", baglayici);
    printf("%-14s%d\n", "kosum", kosum);
    printf("toplam gozlem: %d\n", on_islemci + derleyici + baglayici + kosum);
    return 0;
}
```

```
gozlem                        asama         kanit
makro degeri                  on islemci    KAT metni 3 olarak konuldu
kosullu govde                 on islemci    kisa govde derlemeye girdi
ad birlestirme                on islemci    uretilen ad 7 verdi
baslik metni                  on islemci    bildirimler kaynaga metin olarak kopyalandi
tip boyutu birimi             derleyici     sizeof(char) == 1
sabit ifade katlamasi         derleyici     eleman sayisi 7
numaralandirma sabiti         derleyici     MAVI sabiti 4
islecin tipe gore secimi      derleyici     tam 3 kesirli 3.5
baska birimdeki islev         baglayici     cagri 7 verdi
baska birimdeki ad            baglayici     birim secim olarak bildirildi
baska birimdeki degisken      baglayici     sayac 2
veriden hesaplanan deger      kosum         toplam 43
dizgi uzunlugu                kosum         uzunluk 13
veriye bagli dallanma         kosum         buyuk

on islemci    4
derleyici     4
baglayici     3
kosum         3
toplam gozlem: 14
```

#### Ölçümün Okunuşu

Tablonun dört bölümü dört ayrı bakış açısını gösteriyor. Ön işlemci bölümündeki dört satır
tip bilmez, yalnız metin bilir: makro değeri kaynağa metin olarak kondu, hangi koşullu
gövdenin derlemeye gireceği derleyici kaynağı görmeden belli oldu, iki parçadan yeni bir ad
üretildi ve başlık dosyasının bildirimleri kaynağa kopyalandı. Derleyici bölümündeki dört
satır ise yalnız tek bir birime bakarak karar veriyor: tip boyutunun birimi, sabit ifadenin
katlanması, numaralandırma sabitinin değeri ve bölme işlecinin hangi anlamı taşıyacağı hepsi
`asama.c`'nin kendi içinde çözüldü — hiçbiri `secim.c`'ye bakmadı.

Bağlayıcı bölümündeki üç satır bunun tam tersini gösteriyor: üçü de `secim.c`'de tanımlı bir
adı çağırıyor ve üçü de ancak iki birim bir araya gelince bir anlam kazanıyor. Tek başına
`asama.c` bu üç satırı derleyebilir, ama çalıştıramaz — eksik olan tanımlar yalnız bağlama
aşamasında tamamlanıyor. Koşum bölümündeki üç satır ise yalnız veriye bakıyor: toplam, dizgi
uzunluğu ve veriye bağlı dallanma hiçbiri kaynakta sabit bir sayı olarak durmuyor, üçü de
`main` çalışırken hesaplanıyor. Aynı kaynak farklı bir `veri` dizisiyle çalıştırılsaydı bu
üç satırın sonucu değişirdi; önceki üç bölümde bu mümkün değil, çünkü onların sonucu
kaynağın kendisinden — koşulmadan önce — bellidir.

**DD1.** `gozle` işlevi, `tampon` arabelleği ve dört sayaç (`on_islemci`, `derleyici`,
`baglayici`, `kosum`) çekirdeğin kendi tasarım kararıdır, C'nin bir düzeneği değildir. C'nin
kendisinde "bu satır hangi aşamaya ait" diye soran bir işlev yoktur; hangi satırın hangi
sınıfa girdiğine karar veren şey satırın kaynaktaki konumu ve biçimidir, `gozle` çağrısı
yalnız bu kararı bir satıra yazıp on dört gözlemi tek koşumda karşılaştırılabilir kılar.

**DD2.** Sayaçların dördü de sıradan `int` değişkenidir ve elle artırılır. C'nin çalışma
zamanı, bir satırın derlenirken hangi aşamaya ait olduğunu tutan bir kayıt taşımaz — bu
bilgi koşum başladığında zaten geçmişte kalmıştır. Dağılımı saymanın tek yolu, her gözlemin
yanına doğru sayacı elle eklemektir; sayım da bu yüzden çekirdeğin kaynağında, yani ön
işlemci ile derleyicinin göreceği metinde durur.

On dört gözlemin dördü ön işlemcide, dördü derleyicide, üçü bağlayıcıda, üçü koşumda karara
bağlandı ve çıktı bu dağılımı doğrudan basıyor. Bu sayılar kursun geri kalanı için bağlayıcıdır:
hiçbir ders bu dağılımı değiştirmez, yalnız kendi kesitini alır.

#### Aynı Karar, Dört Yer

Çekirdeğin on dört satırı rastgele seçilmedi; her biri aynı türden bir soruyu farklı bir
aşamada yanıtlıyor: **bir değerin ne olduğu**. `KAT` sabitinin değeri ön işlemcide belli
oluyor, çünkü `#define` bir metin değiştirme kuralıdır ve derleyici kaynağı görmeden önce
uygulanır. `MAVI` sabitinin değeri derleyicide belli oluyor, çünkü bir numaralandırma
(`enum`) üyesi derleme zamanı sabitidir ve tek bir birime bakılarak çözülür. `birim_sayaci`
değişkeninin değeri bağlayıcıda belli oluyor, çünkü değişken `secim.c`'de tanımlı ve
`asama.c` ona yalnız `extern` bir bildirimle erişiyor — ikisi ancak bağlanınca aynı adresi
paylaşıyor. `toplam` değişkeninin değeri ise koşumda belli oluyor, çünkü bir dizinin
elemanları üzerinde döngü yürütülmeden hesaplanamaz.

**DD3.** `enum Renk` üyelerine `1`, `2`, `4` değerlerinin açıkça verilmesi de bir kaynak
kararıdır, dilin zorunlu kıldığı bir şey değildir. Değer verilmeseydi C, üyeleri sıfırdan
başlayarak birer birer artırırdı (`0`, `1`, `2`); burada üyeler bilinçli olarak ikinin
kuvvetleri seçildi ki her biri ayrı bir bit temsil edebilsin. Bu, dördü değil, tek bir
kararı — bir sabitin sayısal değerini — ilgilendiriyor ve o karar da yine derleyici
aşamasında veriliyor.

Dördü de "bir değerin ne olduğu" sorusuna yanıt veriyor, ama dördü de farklı bir yerde
yanıtlıyor. Aynı soruyu sorduğun her seferde hangi aşamanın yanıt vereceğine karar veren
şey, o değeri kaynakta **nasıl yazdığındır**: bir `#define` yazarsan ön işlemci yanıtlar,
bir `enum` yazarsan derleyici yanıtlar, başka bir birimde tanımlı bir ad kullanırsan
bağlayıcı yanıtlar, bir diziyi dolaşırsan koşum yanıtlar. Programcı bu dördü seçebiliyor;
dilin kendisi hiçbirini dayatmıyor.

Bu örüntü kursun ilerleyen derslerinde başka biçimlerde tekrar karşımıza çıkacak. Aynı işi
gören bir hesap Ön İşlemci dersinde bir makro olarak, Fonksiyonlar dersinde bağlayıcının
çözdüğü bildirilmiş bir çağrı olarak yazılabilir; ikisi de "bu değer nedir" sorusuna yanıt
verir, ama biri metin düzeyinde, öbürü birimler bir araya gelince. Kurs boyunca sorulacak
soru değişmiyor: kaynakta yazılan bu karar hangi elde veriliyor?

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

Dört aşamanın varlığı bir karmaşıklık değil, dört ayrı denetim noktasıdır. Aynı türden bir
yanlışlık dört ayrı yerde ortaya çıkabilir ve dördü de aynı ağırlığı taşımaz. `KAT`
makrosuna yanlış bir sayı yazılırsa metin yanlış üretilir ve bu, kaynağın geri kalanına
sessizce yayılır — ön işlemci hiçbir denetim yapmaz, yalnız değiştirir. `MAVI` sabitine
yanlış bir değer verilirse derleyici bunu görebilir, çünkü tek bir birimin içinde kalan bir
tutarsızlıktır. `secim.c`'deki bir tanım silinirse hata derleyicide değil bağlayıcıda çıkar,
çünkü `asama.c` kendi başına derlenmeye devam eder — eksik olan yalnız birimler bir araya
gelince görünür. `veri` dizisine yanlış bir sayı eklenirse hiçbir aşama bunu yakalamaz;
sonuç yalnız o veriyle koşulunca ortaya çıkar.

Dördü de "yanlış değer" diye anılabilir, ama dördünün yakalandığı yer ayrıdır ve dördü de
aynı gücü taşımaz. Ön işlemci ve derleyici hatası programın derlenmesini durdurabilir;
bağlayıcı hatası programın bağlanmasını durdurabilir; koşum hatası ise yalnız o veriyle
karşılaşıldığında görünür ve önceki üç aşamanın hiçbiri onu göremez. Aşama sayısının
fazlalığı bu yüzden bir yük değil, bir avantajdır: aynı hata sınıfının nerede yakalanacağını
seçmek, kaynağı nasıl yazdığına bağlıdır.

Bu sınır kursun geri kalanında bir uyarı olarak da okunmalı: bir aşamanın denetlemediği bir
şey, bir sonraki aşamanın denetleyeceği anlamına gelmez. Ön işlemcinin geçtiği bir hata
derleyiciye kalmaz; derleyicinin geçtiği bir hata bağlayıcıya kalmaz; bağlayıcının geçtiği
bir hata da koşuma kalmaz — her aşama yalnız kendi girdisine bakar ve bir öncekinin ne
yaptığını sorgulamaz. Dört aşamanın toplamı bu yüzden birbirini tamamlayan bir denetim
zinciri değil, dördü de kendi sınırları içinde çalışan dört ayrı bakıştır.

#### Özet

- Bir C kaynağı programa dönüşürken dört aşamadan geçer: ön işlemci, derleyici, bağlayıcı,
  koşum; bu dört ad kurs boyunca değişmez.
- Üç dosyalık ölçüm çekirdeği on dört gözlemi tek koşumda sınıflandırdı: 4'ü ön işlemci,
  4'ü derleyici, 3'ü bağlayıcı, 3'ü koşum.
- Aynı türden bir karar (bir değerin ne olduğu) dört aşamanın herhangi birinde verilebilir;
  hangisinin seçildiği kaynağın nasıl yazıldığına bağlıdır.
- Dört aşamanın varlığı karmaşıklık değil denetim noktası çokluğudur: aynı tür hata dört
  ayrı yerde, dört ayrı ağırlıkla yakalanabilir.

#### Sonraki Adım

Bu ders dört aşamayı adlandırdı ve tek bir koşumda yan yana gösterdi. Sıradaki ders aynı
çekirdeğe geri dönüyor, ama bu kez dört sınıfı tek tek ele alıyor: her aşamanın kararını
verirken **hangi girdiye baktığı** ve aşamaların neden geri dönüşü olmayan bir sırayla
çalıştığı.

### Derleme Aşamaları

Source: https://academia.sh/tr/kurslar/c-temelleri/derleme-asamalari

Dört aşamanın her biri farklı bir girdiye bakar: ön işlemci yalnız metne, derleyici tek bir birime, bağlayıcı birden çok birime, koşum ise yalnız veriye. Küçük bir ölçüm kesitiyle dördü yan yana koşturuluyor; ayrı bir çift program da aynı kaynağın bir satırının, kaynakta önceden verilmiş bir kararla iki farklı derlemeye nasıl girdiğini gösteriyor.

Önceki ders dört aşamayı adlandırdı: ön işlemci, derleyici, bağlayıcı, koşum; üç dosyalık
çekirdeği tek koşumda çalıştırıp on dört gözlemi bu dört sınıfa dağıttı. Bu ders aynı dört
adı, her birinin karar verirken **hangi girdiye baktığı** sorusuyla derinleştiriyor.
Dört aşama aynı kaynağa bakar, ama hiçbiri aynı şeyi görmez — biri yalnız metin görür, biri
tek bir dosya, biri birden çok dosyanın kesişimi, biri de yalnız o an elindeki veri. Bu fark
soyut bir sınıflandırma değildir; her aşamanın elinde gerçekten o girdiden başka hiçbir şey
yoktur ve bu ders bunu ölçerek gösterecek.

#### Dört Girdi

Her aşamanın kararını verirken elinde olan şey farklıdır:

| Sınıf | Baktığı girdi |
|---|---|
| ön işlemci | Yalnız kaynağın metni; tip, sabit, birim kavramlarının hiçbiri henüz yok |
| derleyici | Tek bir çeviri biriminin tamamı; o birimin dışına bakmaz |
| bağlayıcı | Birden çok birimin ürettiği tanımlar; içeriklerini yorumlamaz, yalnız adları eşler |
| koşum | Programın kendi verisi; kaynağın metni artık elde değildir |

Bu sıralama aynı zamanda bir daralma sırasıdır. Ön işlemcinin elindeki bilgi en genişidir —
bütün kaynak metnine erişebilir, ama içindeki hiçbir yapıyı anlamaz. Derleyici bu metnin
yalnız bir biriminden sorumludur, ama artık tip ve ifade gibi yapıları görür. Bağlayıcı tek
bir birimi bile görmez; yalnız birimlerin dışa açtığı adları görür. Koşum ise kaynağın hiçbir
parçasını görmez, yalnız programın o an elindeki veriyi görür.

Bu daralma tek yönlüdür: bir aşama, bir öncekinin kaybettiği bilgiyi geri kazanamaz. Bir
makro adı ön işlemcide metne dönüşür ve derleyici o adı hiç görmez — gördüğü yalnız yerine
konmuş metindir, "bu bir makroydu" bilgisi kaynakta iz bırakmaz. Aynı şekilde bir değişkenin
tipi derleyicide ikili bir düzene (nesne dosyasındaki bir sembole) indirgenir; bağlayıcı bu
sembolü yalnız adıyla eşler, o adın hangi tipten bir değişkene ait olduğunu bilmez ve
bilmesine gerek de yoktur — tip uyuşmazlığı varsa onu derleyici zaten kendi aşamasında
yakalamıştır. Her aşama, kendinden öncekinin işini bitirdiğini varsayarak çalışır.

Bu güven zinciri kaynağın kendisiyle sınırlı değildir. Bağlayıcı, girdisi olan nesne
dosyalarının doğru bir derleyiciden geldiğini de varsayar; herhangi bir aracın ürettiği,
aynı biçimi taşıyan bir nesne dosyası aynı şekilde kabul edilir. Bu, dört aşamanın
sırasının neden bir uygulamaya değil bir **biçime** bağlı olduğunu gösteriyor — her aşama
kendinden önceki adımın hangi araçla yürütüldüğünü değil, ürettiği biçimin sözleşmeye uyup
uymadığını denetler.

#### Dört Sınıf, Dört Satır

Aşağıdaki kesit önceki dersin çekirdeğinden dört satır alıyor — her sınıftan bir tane — ve
yalnız bu dördünü yan yana koşturuyor. Bağlayıcı sınıfını göstermek için yine iki birim
gerekiyor.

```c
// asama.h — baska birimde tanimli tek adin bildirimi
#ifndef ASAMA_H
#define ASAMA_H

int kimlik(void);

#endif
```

`asama.h` kendi başına ne derlenir ne de bir birim sayılır — bir dosyayı bir birim yapan şey,
onun `cc` komutuna ayrı bir kaynak olarak verilmesidir, `#include` ile başka bir dosyanın
içine kopyalanması değil. Bu ders boyunca `asama.h` doğrulayıcıya yalnız KAYNAK olarak
görünecek: içeriği ancak onu `#include` eden birimin metnine karıştıktan sonra derleyicinin
önüne gelir. Başlık dosyasının bu paylaşım rolü sıradaki dersin konusu.

```c
// secim.c — ikinci birim: tanim burada, cagri baglamada cozulur
#include "asama.h"

int kimlik(void) {
    return 7;
}
```

```c
// asama.c — dort sinifi yan yana olcen kucuk kesit: her sinieftan tek satir
#include <stdio.h>
#include "asama.h"

#define KAT 3

static void gozle(const char *ad, const char *sinif, const char *kanit) {
    printf("%-30s%-14s%s\n", ad, sinif, kanit);
}

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

    printf("%-30s%-14s%s\n", "gozlem", "asama", "kanit");

    snprintf(tampon, sizeof tampon, "KAT metni %d olarak konuldu", KAT);
    gozle("makro degeri", "on islemci", tampon);

    int tam = 7 / 2;
    double kesirli = 7 / 2.0;
    snprintf(tampon, sizeof tampon, "tam %d kesirli %.1f", tam, kesirli);
    gozle("islecin tipe gore secimi", "derleyici", tampon);

    snprintf(tampon, sizeof tampon, "cagri %d verdi", kimlik());
    gozle("baska birimdeki islev", "baglayici", tampon);

    int veri[] = {4, 8, 15, 16};
    int toplam = 0;
    for (size_t i = 0; i < sizeof veri / sizeof veri[0]; i++) {
        toplam += veri[i];
    }
    const char *karar = (toplam > 40) ? "buyuk" : "kucuk";
    gozle("veriye bagli dallanma", "kosum", karar);

    return 0;
}
```

```
gozlem                        asama         kanit
makro degeri                  on islemci    KAT metni 3 olarak konuldu
islecin tipe gore secimi      derleyici     tam 3 kesirli 3.5
baska birimdeki islev         baglayici     cagri 7 verdi
veriye bagli dallanma         kosum         buyuk
```

**DD4.** Bu kesit çekirdeğin on dört satırından yalnız dördünü alıyor ve `on_islemci`,
`derleyici`, `baglayici`, `kosum` sayaçlarını hiç taşımıyor — burada amaç dağılımı yeniden
saymak değil, dört sınıfı tek bakışta yan yana koymak. Sayım kesin olarak yalnız önceki
derste yapılır ve orada verilen 4/4/3/3 dağılımı burada değişmez.

Dört satırın her biri, bir önceki bölümdeki "baktığı girdi" tanımını doğruluyor. `makro
degeri` satırı `KAT`in sayısal değerini hiç görmeden, yalnız `3` metnini yerine koyarak
üretildi — ön işlemci `KAT`in bir tam sayı sabiti olduğunu bile bilmez. `islecin tipe gore
secimi` satırı `asama.c`nin kendi içindeki iki değişkenin tipine bakarak üretildi —
`secim.c`nin var olup olmadığının bir önemi yok. `baska birimdeki islev` satırı ise tam
tersini gösteriyor: `kimlik`in gövdesi `asama.c`de hiç yok, yalnız bir bildirim var; kanıt
ancak `secim.c` ile bağlanınca ortaya çıkıyor. `veriye bagli dallanma` satırı da kaynakta
hiçbir sabit sayı taşımıyor; `buyuk` ya da `kucuk` yalnız `veri` dizisinin içeriğine bakılarak
koşumda hesaplanıyor.

Dört satırın hangi sırayla üretildiği de aşamaların sırasını yansıtıyor. Program çalışmaya
başladığında `asama.c` çoktan derlenmiş ve `secim.c` ile bağlanmıştır; koşum yalnız bu bitmiş
programın içinde ilerler. `gozle` çağrılarının kaynaktaki sırası ile çıktının satır sırası
aynı olsa da, bu bir zorunluluk değil bir gözlemdir: dört satırın hangi aşamada karar
verildiği, çıktıda hangi sırada durduklarından bağımsızdır.

İki satır birbirine yakın durabilir ve yine de farklı sınıflara girebilir. Önceki dersin tam
çekirdeğinde `makro degeri` ile `numaralandirma sabiti` ilk bakışta aynı işi yapıyor gibi
görünür — ikisi de bir "sabit değer" üretir. Ama `KAT` bir `#define` olduğu için değeri
kaynağa metin olarak konur ve ön işlemci bunu derleyici hiç devreye girmeden yapar; `MAVI` ise
bir `enum` üyesi olduğu için değeri derleme zamanı sabit ifadesi olarak hesaplanır ve bu
hesap `asama.c`nin kendi birim sınırları içinde kalır. İkisi de kaynakta sabit görünür, ikisi
de koşumda değişmez, ama biri metin düzeyinde bitmiştir, öbürü derleyicinin tip ve ifade
kurallarından geçer. Bu fark, "sabit görünen her şey aynı sınıfa girer" varsayımının neden
yanlış olduğunu gösteriyor: sınıfı belirleyen görünüş değil, kaynaktaki yazım biçimidir.

#### Aşamaların Birbirine Kapalılığı

Bir aşamanın diğerine kapalı olması, aralarında bir iletişim kanalı olmadığı anlamına gelir.
Derleyici, ön işlemcinin bir metni neden öyle ürettiğini sormaz; yalnız eline geleni kabul
eder. Bağlayıcı, derleyicinin bir tipi nasıl kontrol ettiğini bilmez; yalnız üretilen
sembolün adına bakar. Koşum ise kaynağın hangi satırdan geldiğini artık taşımaz; elindeki
yalnız makine komutları ve verinin kendisidir.

Bu kapalılığın somut bir sonucu var: bir aşama, bir sonrakine yalnız **kendi çıktısını**
bırakabilir, kendi süreciyle ilgili hiçbir ek bilgi taşıyamaz. Ön işlemci `KAT`in bir makro
olduğunu derleyiciye söylemez, çünkü söyleyecek bir kanalı yoktur — derleyicinin gördüğü
yalnız `3` sayısıdır, sanki kaynakta hep öyle yazılmış gibi. Derleyici de `kimlik`in başka
bir birimde tanımlı olduğunu bağlayıcıya "söylemez"; yalnız kendi ürettiği nesne dosyasına,
çözülmemiş bir ad bırakır ve bağlayıcı bu adı kendi yöntemiyle arar. Her geçişte bir önceki
aşamanın **süreci** kaybolur, yalnız **sonucu** kalır.

Bu, dört sınıfın neden birbirine karışmadığını da açıklıyor. Bir gözlemin hangi sınıfa
girdiğini anlamak için "hangi aşama bu kararı verebilirdi" diye sormak yeterlidir: bir metin
değişimiyse ön işlemci, bir tip veya sabit ifadeyse derleyici, birimler arası bir adsa
bağlayıcı, veriye bakan bir koşulsa koşum. Bu ölçüt yalnız bu derste değil, kursun geri
kalanında da aynı biçimde çalışacak.

Bağlayıcının "yalnız adlara bakması" da aynı kapalılığın bir sonucu. `kimlik` adı `asama.c`
tarafında yalnız bir bildirim olarak durur, `secim.c` tarafında ise bir tanım olarak durur;
bağlayıcının işi bu iki tarafı aynı ad üzerinden eşlemektir. Bunu yaparken `kimlik`in ne iş
yaptığına, gövdesinin ne içerdiğine hiç bakmaz — yalnız adın her iki tarafta da var olup
olmadığını ve varsa kaç kez tanımlandığını denetler. Bir bildirim ile bir tanımın nasıl
ayrıldığı ve bağlayıcının bu ikisini nasıl bir araya getirdiği, sıradaki dersin konusu.

Koşum sınıfının kapalılığı ise en köktenidir. Program çalışmaya başladığında elinde kaynağın
hiçbir izi kalmaz; `veriye bagli dallanma` satırının hangi kaynak satırından geldiğini bilen
hiçbir mekanizma yoktur, çalışan program yalnız kendi verisine ve o verinin ürettiği
sonuçlara bakar. Bu yüzden bir koşum kararını "geriye izleyip" kaynaktaki karşılığını bulmak,
öteki üç aşamadaki gibi doğrudan değildir — kaynakla koşum arasındaki bağ, derleme ve bağlama
sırasında geri dönüşsüz biçimde bir kere kurulmuştur.

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

Aşamalar sırayla çalışır ve geri dönmez: ön işlemcinin ürettiği metin yanlışsa derleyici onu
düzeltmez, yalnız sonucunu görür. Bunu göstermenin en doğrudan yolu, aynı koşullu gövdeyi iki
kez derlemektir — aradaki tek fark kaynağa eklenen bir satır.

Kaynağın farklı gövdeler arasında `#ifdef` ile seçim yapması yaygın bir örüntüdür ve seçim
her zaman ön işlemcide, derleme daha başlamadan karara bağlanır. Önceki dersteki `KAT`
makrosu ve `BIRLESTIR` ad birleştirmesi de aynı sınıftandı: ikisi de derleyici kaynağı
görmeden önce, metin düzeyinde sonuca ulaşmıştı. Aradaki fark şu — `KAT` her zaman aynı
metne açılıyordu, ama `AYRINTILI` tanımlı olup olmaması kaynağın **hangi dalının** derlemeye
gireceğini belirliyor. Bu, tek bir makro değerinden daha güçlü bir örnek: burada seçilen şey
bir sayı değil, kaynağın kendisinin bir parçası.

```c
// gorunum.c — govde secimi kaynakta bir #define ile onceden karara baglanir
#include <stdio.h>

/* AYRINTILI burada tanimli degil: on islemci kisa govdeyi derlemeye sokar. */

int main(void) {
    printf("kosullu govde: ");
#ifdef AYRINTILI
    printf("ayrintili govde derlemeye girdi\n");
#else
    printf("kisa govde derlemeye girdi\n");
#endif
    return 0;
}
```

```
kosullu govde: kisa govde derlemeye girdi
```

```c
// gorunum.c — ayni kosullu blok, govde secimini degistiren tek satir eklendi
#include <stdio.h>

#define AYRINTILI

int main(void) {
    printf("kosullu govde: ");
#ifdef AYRINTILI
    printf("ayrintili govde derlemeye girdi\n");
#else
    printf("kisa govde derlemeye girdi\n");
#endif
    return 0;
}
```

```
kosullu govde: ayrintili govde derlemeye girdi
```

**DD5.** İki dosya da aynı adı taşıyor (`gorunum.c`) ve ikinci blok birinciyi diskte
değiştiriyor; bu, iki ayrı derleme olduğunu, aynı programın iki farklı çalışması olmadığını
göstermek içindir. Aradaki tek fark, ikinci dosyanın başına eklenen tek bir `#define`
satırıdır — gövdenin geri kalanı harfi harfine aynıdır.

`#ifdef` burada koşullu derleme (conditional compilation) ailesinin tek bir üyesidir; aynı
ailede bir adın tanımlı **olmadığını** sınayan `#ifndef` (`asama.h`'nin çifte içermeyi
engellemek için kullandığı yönerge de budur), bir ifadenin değerini sınayan `#if`, başka bir
dalı ekleyen `#elif` ve hiçbirinin doğru olmadığı durumu karşılayan `#else` bulunur. Hepsi
aynı sınıfa girer, çünkü hepsi derleyici devreye girmeden, yalnız ön işlemcinin elindeki
tanımlı adlara bakarak karar verir.

İki derlemenin de kaynağı, satır satır bakıldığında, `#ifdef` bloğunun iki dalını da taşıyor.
Ama derleyici hiçbir zaman iki dalı da görmez: ön işlemci, `AYRINTILI` tanımlı mı diye bakar
ve seçilmeyen dalı **kaynaktan tümüyle siler** — derleyiciye giden metinde o dal artık yoktur,
yorumlanmamış ya da atlanmış değil, hiç yazılmamış gibidir. Derleyici bu yüzden "iki
seçenekten birini eledi" demez; onun gördüğü kaynakta zaten tek bir gövde vardır. Bir
sonraki aşamaya (derleyiciye) geçildiğinde ön işlemcinin verdiği karar artık sorgulanamaz;
tek yol kaynağı değiştirip yeniden en baştan başlamaktır — iki blok arasındaki fark da tam
olarak budur.

Bu, aşamaların neden bir zincir değil bir **tek yönlü sıra** olduğunu gösteriyor. Bir zincirde
her halka bir öncekini denetleyebilir; burada hiçbir aşama bir öncekinin verdiği kararı geri
alamaz, yalnız onun sonucunu devralır. Derleme Aşamaları dersinin adı da buradan geliyor:
aşama, geri dönülebilen bir adım değil, ardışık ve tek yönlü bir sıradır.

Bu tek yönlülüğün pratik bir sonucu var: bir programın verdiği yanlış sonucu ararken, önce o
sonucun hangi aşamada karara bağlandığını sormak aramayı daraltır. `kosullu govde` satırının
yanlış çıktığı görülürse aranacak yer derleyici ya da bağlayıcı değil, kaynaktaki `#define`
satırıdır — çünkü derleyicinin önüne gelen metin zaten tek bir gövdeye indirgenmiştir ve
derleyici o metni doğru yorumlamaktan başka bir şey yapmamıştır. Aynı mantık öteki üç sınıf
için de geçerlidir: bir sınıfın ürettiği sonucun ardında yalnız o sınıfın baktığı girdi
vardır, öncesi değil.

#### Özet

- Dört aşama aynı kaynağa bakar ama farklı girdiler görür: ön işlemci metin, derleyici tek
  birim, bağlayıcı birden çok birimin tanımları, koşum ise yalnız veri.
- Küçük bir kesit dört sınıftan birer satırı yan yana koşturdu; her satır kendi sınıfının
  baktığı girdiyi doğruladı.
- Aşamalar sırayla çalışır ve geri dönmez: ön işlemcinin elediği bir dal derleyiciye hiç
  ulaşmaz, derleyici onu "eledi" olarak değil, hiç yokmuş gibi görür.
- Bir aşamanın kararını değiştirmenin tek yolu kaynağı değiştirip zinciri en baştan
  koşturmaktır; hiçbir aşama bir sonrakinden geri dönüş bilgisi almaz.
- Bir hatanın hangi aşamaya ait olduğunu bilmek aramayı daraltır: sonucun ardında yalnız o
  aşamanın baktığı girdi vardır, öncesinde olan biten değil.

#### Sonraki Adım

Bağlayıcı sınıfı bu derste yalnız bir çağrıyla gösterildi: `kimlik`in bildirimi bir dosyada,
tanımı başka bir dosyadaydı ve `asama.h` bu bildirimi ikisi arasında taşıyan araç oldu.
Sıradaki ders bu paylaşımın nasıl kurulduğuna bakıyor — bir başlık dosyasının bir bildirimi
birden çok birime nasıl taşıdığı, bildirim ile tanımın neden ayrı tutulması gerektiği ve
bunların birbirine karıştırılmasının nereye kadar zararsız kaldığı.

### Başlık Dosyaları

Source: https://academia.sh/tr/kurslar/c-temelleri/baslik-dosyalari

Bir başlık dosyası (header file) bir adı taşımaz, o adın metnini taşır ve ön işlemci onu her kullanan birime ayrı ayrı kopyalar. Bu, aynı bildirimin iki birime birden girmesini ve ikisinin de derlenmesini mümkün kılar; ölçüm bunu koşturarak, çifte içerme korumasının neyi engellediğini de ayrıca göstererek kanıtlıyor.

Önceki ders bağlayıcı sınıfını yalnız bir çağrıyla gösterdi: `kimlik`in bildirimi bir
dosyada, tanımı başka bir dosyadaydı ve aradaki köprüyü `asama.h` kurdu. Bu ders o köprünün
kendisine bakıyor. Bir başlık dosyası (header file) ne bir birimdir ne de kendi başına
derlenir; yalnız metin taşır ve o metin, onu `#include` eden her birime ayrı ayrı kopyalanır.

Bu tek cümle, kursun geri kalanında sık hatırlanacak bir gerçeği özetliyor: C'de bir birim
kendi kaynağıyla `#include` ettiği her şeyin **birleşimidir**. Derleyicinin gördüğü şey,
`asama.c` dosyasının ekranda göründüğü hâli değil, ön işlemcinin bütün `#include`leri
çözdükten sonra ürettiği tek parça metindir. Başlık dosyası bu birleşimin neresinde
durduğunu belirler ve bu ders o konumun neden yalnız bildirimlere ayrılması gerektiğini
gösteriyor.

#### Bildirim ile Tanım

C'de bir adın kaynakta iki farklı görünümü vardır. **Bildirim (declaration)** bir adın var
olduğunu, tipini ve nasıl çağrılacağını söyler, ama bir gövde vermez. **Tanım (definition)**
ise o adın karşılığını — bir işlevin gövdesini, bir değişkenin ayrılmış yerini — verir. Bir
ad birden çok yerde bildirilebilir, ama yalnız bir yerde tanımlanabilir; bu ayrım, başlık
dosyalarının neden var olduğunun gerekçesidir.

Bir birim, çağırdığı her adın tanımını görmek zorunda değildir; yalnız bildirimini görmesi
yeter — bu, tek bir başlık dosyasının onlarca birim arasında paylaşılabilmesinin nedenidir. Derleyici bir çağrıyı üretmek için adın tipini, kaç parametre aldığını ve ne
döndürdüğünü bilmelidir — bunların hepsi bildirimde vardır. Adın gerçek karşılığının nerede
olduğu, derleyiciyi ilgilendirmeyen bir sorudur; o soruyu bağlayıcı yanıtlayacaktır. Bu
ayrım sayesinde bir birim, hiç görmediği bir tanımı çağırabilir — yeter ki doğru bir
bildirime sahip olsun.

```c
// asama.h — baska birimlerde tanimli adlarin bildirimi
#ifndef ASAMA_H
#define ASAMA_H

/* Bu iki islev baska bir birimde tanimli: cagrilari baglama asamasinda cozulur. */
int kimlik(void);
const char *birim_adi(void);

/* Bu degisken de baska bir birimde tanimli. */
extern int birim_sayaci;

#endif
```

`asama.h` üç adı **bildiriyor**, hiçbirini **tanımlamıyor**. `kimlik` ve `birim_adi`nin
gövdesi yok; `birim_sayaci`nin başında duran `extern` de tam olarak bunu söylüyor — "bu ad
başka bir yerde tanımlı, burada yalnız bildiriliyor." Tanımların kendisi ikinci birimde
duruyor.

```c
// secim.c — ikinci birim: tanimlar burada, cagrilar baglamada cozulur
#include "asama.h"

int birim_sayaci = 2;

int kimlik(void) {
    return 7;
}

const char *birim_adi(void) {
    return "secim";
}
```

Üçüncü dosya `asama.h`yi kendi başına, `secim.c`den habersiz olarak dahil ediyor ve üçünü de
çağırıyor.

```c
// asama.c — baslik dosyasinin tasidigi bildirimlerle ikinci birime baglanan kesit
#include <stdio.h>
#include "asama.h"

static void gozle(const char *ad, const char *sinif, const char *kanit) {
    printf("%-30s%-14s%s\n", ad, sinif, kanit);
}

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

    printf("%-30s%-14s%s\n", "gozlem", "asama", "kanit");

    gozle("baslik metni", "on islemci", "bildirimler kaynaga metin olarak kopyalandi");

    snprintf(tampon, sizeof tampon, "cagri %d verdi", kimlik());
    gozle("baska birimdeki islev", "baglayici", tampon);

    snprintf(tampon, sizeof tampon, "birim %s olarak bildirildi", birim_adi());
    gozle("baska birimdeki ad", "baglayici", tampon);

    snprintf(tampon, sizeof tampon, "sayac %d", birim_sayaci);
    gozle("baska birimdeki degisken", "baglayici", tampon);

    return 0;
}
```

```
gozlem                        asama         kanit
baslik metni                  on islemci    bildirimler kaynaga metin olarak kopyalandi
baska birimdeki islev         baglayici     cagri 7 verdi
baska birimdeki ad            baglayici     birim secim olarak bildirildi
baska birimdeki degisken      baglayici     sayac 2
```

**DD6.** `asama.h`nin kendisi hiçbir gözlemde `on islemci` etiketiyle görünmüyor; onun yerine
görünen `baslik metni` satırı bir **sonuçtur**, `asama.h`nin kendisi değil. Ön işlemcinin
yaptığı iş bir metin kopyalamaktan ibarettir: `#include "asama.h"` satırı, derleyici devreye
girmeden önce, `asama.h`nin içeriğiyle **değiştirilir**. `asama.c`de görünen `kimlik(void)`
bildirimi bu yüzden hiçbir zaman `asama.c`nin kaynağında elle yazılmadı — kopyalanarak oraya
geldi. Aynı satır `secim.c`nin kaynağına da kopyalandı; iki kopya birbirinden bağımsız,
ikisi de kendi biriminin bir parçası oldu. Bir dosyayı düzenlerken görünen `#include`
satırı, derlenen metnin yalnız bir özetidir — gerçek kaynak, ön işlemci çalıştıktan sonra
ortaya çıkan, gözle hiç görülmeyen genişletilmiş metindir.

`secim.c` de `asama.h`yi dahil ediyor ve aynı üç bildirim onun kaynağına da kopyalanıyor.
İki birim de bu bildirimlerle derleniyor ve ikisi de uyarısız derleniyor — aynı bildirim iki
ayrı yere girdi ve ikisi de bundan rahatsız olmadı, çünkü bildirim bir söz verir, bir yer
ayırmaz. Tanım ise yalnız `secim.c`de bir kez var; `asama.c` üç adı da kullanıyor ama
hiçbirinin tanımını taşımıyor, tanımları bağlama aşamasında ödünç alıyor.

Çıktıdaki dört satır bu ödünç almanın nasıl işlediğini gösteriyor. `baslik metni` satırı
kaynağın kendisiyle ilgili bir gözlem, çünkü tek bir metnin bildirim taşıdığını kanıtlıyor —
ama önemli olan `asama.c`nin bu üç adı kullanabilmesi. `baska birimdeki islev` ve `baska
birimdeki ad` satırları `secim.c`nin işlev tanımlarına ulaşıyor; `baska birimdeki degisken`
satırı ise bir değişken tanımına ulaşıyor. Üçü de aynı köprüyü kullanıyor: `asama.h`deki
bildirim, `asama.c`ye "bu ad var, işte tipi" diyor; gerçek karşılığı yalnız bağlama sırasında
geliyor. `asama.c` tek başına derlenirken `birim_sayaci`nin değerinin `2` olduğunu bile
bilmiyor — bildiği yalnız böyle bir tam sayının bir yerde var olduğu.

#### Başlık Neyi Paylaşır, Neyi Paylaşmaz

`asama.h`nin yalnız bildirim taşıması bir seçim değil, bir zorunluluktur. Bir başlık dosyası
birden çok birime dahil edilebildiği için, içine bir işlev **tanımı** konsaydı — gövdesiyle
birlikte — o gövde her onu dahil eden birime ayrı ayrı kopyalanırdı. `asama.h`yi hem
`asama.c` hem `secim.c` dahil ediyor; eğer `kimlik`in gövdesi orada dursaydı, iki birim de
aynı adı tanımlamış olur ve bağlama aşamasında "aynı ad iki kez tanımlı" çakışması çıkardı.
Tanımın başlıkta değil, yalnız bir birimde durması bu yüzden zorunludur — çifte içerme
koruması bunu tek bir birim **içinde** önler, ama bir tanımın birden çok birime yayılmasını
önlemez; onu önleyen şey, tanımı hiç başlığa koymamaktır.

Bu, `extern int birim_sayaci;` satırının neden `int birim_sayaci = 2;` olarak yazılmadığını
da açıklıyor. `extern` anahtar sözcüğü derleyiciye "burada yer ayırma, bu ad başka bir yerde
zaten var" der; onsuz yazılan bir değişken bildirimi aslında bir tanımdır ve bir yer ayırır.
Başlığa `extern`siz bir değişken konsaydı, onu dahil eden her birim kendi kopyasını
ayırırdı — `asama.c` ile `secim.c` aynı adı taşıyan ama farklı adreslerde duran iki ayrı
`birim_sayaci` elde ederdi, bu da bağlama aşamasında ya çakışma ya da sessiz bir
tutarsızlık doğururdu.

Bu kural, işlev bildirimleri için doğrudan geçerli değildir; bir işlev bildirimi zaten kendi
başına bir yer ayırmaz, yalnız `extern`li bir değişken bildirimiyle aynı rolü oynar. Kuralın
özü şu: bir başlığa konan her satır, onu okuyan **her** birim tarafından aynı şekilde
yorumlanmalıdır. Bir bildirim bu koşulu doğal olarak sağlar, çünkü kopyalandığı her yerde
aynı sözü verir; bir tanım sağlamaz, çünkü kopyalandığı her yerde ayrı bir yer talep eder.

#### Çifte İçerme Koruması

Bir başlık dosyası aynı birime birden fazla yoldan girebilir — doğrudan, ya da başka bir
başlık dosyasının içinden. Bu, tek bir dosyanın iki kez `#include` edilmesi kadar açık bir
hata olmak zorunda değil; çoğu zaman iki ayrı başlık, üçüncü ortak bir başlığa aynı anda
bağımlı olduğunda kendiliğinden ortaya çıkar. Bir birim ikisini de dahil ettiğinde, ortak
başlık o birime iki kez girmiş olur. `asama.h`nin başındaki `#ifndef` / `#define` çifti tam
olarak bunu karşılamak için var. Aşağıdaki küçük örnek bunu, `asama.h`den bağımsız bir
başlıkla gösteriyor.

```c
// sinir.h — bir tip bildiren kucuk baslik; cifte icerme korumasi tasiyor
#ifndef SINIR_H
#define SINIR_H

typedef struct {
    int satir;
    int sutun;
} Konum;

#endif
```

```c
// giris.h — sinir.h'yi bir yoldan ceken ara baslik
#include "sinir.h"
```

```c
// cikis.h — sinir.h'yi baska bir yoldan ceken ikinci ara baslik
#include "sinir.h"
```

```c
// baglam.c — sinir.h iki ayri yoldan ayni birime giriyor
#include <stdio.h>
#include "giris.h"
#include "cikis.h"

int main(void) {
    Konum k = {3, 7};
    printf("konum: satir %d sutun %d\n", k.satir, k.sutun);
    printf("baslik iki yoldan cekildi, tip bir kez tanimlandi\n");
    return 0;
}
```

```
konum: satir 3 sutun 7
baslik iki yoldan cekildi, tip bir kez tanimlandi
```

**DD7.** `baglam.c` `sinir.h`yi hiç doğrudan dahil etmiyor — `giris.h` ve `cikis.h` üzerinden,
iki ayrı yoldan dolaylı olarak çekiyor. Ön işlemci her iki `#include`i de sırayla işler:
birincisinde `SINIR_H` henüz tanımlı olmadığı için `Konum` tipi kaynağa kopyalanır ve
`SINIR_H` tanımlanır; ikincisinde `#ifndef SINIR_H` artık yanlıştır ve `#endif`e kadar olan
her şey **atlanır** — `Konum` bir daha kopyalanmaz. Koruma olmasaydı `Konum` aynı birimde iki
kez tanımlanmış olurdu ve bu derlenmezdi. Buradaki yapı **etiketsizdir**: C, etiketsiz her
yapı bildirimini ayrı bir tip sayar, bir `typedef` adı ise ancak **aynı** tipe yeniden
bağlanabilir. İkinci kopya `Konum`u ikinci ve farklı bir anonim tipe bağlamaya çalışır, kural
çiğnenir. Tanım, bildirimin aksine, tekil olmak zorundadır.

Bu, `#ifndef` ailesinin bildirimle değil **tanımla** ilgili bir sorunu çözdüğünü gösteriyor.
`kimlik`in bildirimi iki birime kopyalanabiliyordu, çünkü bildirim çoğaltılabilir. `Konum`un
tanımı ise aynı birime iki kez kopyalanamaz, çünkü tanım tekildir — tıpkı bir işlevin gövdesi
gibi. Çifte içerme koruması, bir başlığın kendi içindeki tanımları bu tekillik kuralından
korur.

**DD8.** `SINIR_H` adının kendisinin özel bir anlamı yok; yalnız "bu başlık daha önce
kopyalandı mı" sorusuna yanıt veren bir işaretçi görevi görüyor. Ön işlemcinin gördüğü şey
bir dosya adı ya da bir yol değil, yalnız bu adın tanımlı olup olmadığıdır — `#define SINIR_H`
satırı çalıştığı andan itibaren, aynı birimdeki her sonraki `#ifndef SINIR_H` yanlış sonuç
verir. Koruma bu yüzden dosyanın kendisine değil, kaynağa bırakılan bu tek işarete dayanır;
adı çakışan başka bir başlık aynı işareti kullansaydı, koruma yine çalışır ama bu kez yanlış
başlığı dışarıda bırakırdı. Adın başlığa özgü ve tekil olması bu yüzden programcının
sorumluluğudur, dilin garantisi değildir.

`giris.h` ve `cikis.h`nin kendileri hiçbir yeni bildirim taşımıyor; ikisi de yalnız
`sinir.h`ye birer köprü. Bu, gerçek başlık ağaçlarında sık görülen bir örüntüyü küçük
ölçekte yansıtıyor: bir başlık kendi ihtiyaç duyduğu başka başlıkları dahil eder ve onu
kullanan birim, bu iç bağımlılıkların farkında olmak zorunda kalmaz. `baglam.c` `sinir.h`nin
var olduğunu bile bilmeden `Konum` tipine erişti — `giris.h` ve `cikis.h`nin ikisi de bu
bağımlılığı kendi başına taşıdı.

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

Başlık dosyası bir sözleşme değildir. `asama.h`deki bildirim `secim.c`deki tanımla birebir
örtüşüyor çünkü bu çekirdekte ikisi elle tutarlı tutuldu — ama dilin kendisi bu tutarlılığı
denetlemez. `asama.h` `int kimlik(void);` yerine yanlışlıkla `int kimlik(int);` bildirseydi,
`secim.c`deki tanım hâlâ `int kimlik(void) { return 7; }` olurdu. `asama.c`yi derleyen
derleyici yalnız `asama.h`deki bildirime bakar ve çağrıyı ona göre üretir; `secim.c`yi
derleyen derleyici de yalnız kendi tanımına bakar. İkisi de kendi biriminin içinde
tutarlıdır, ikisi de tek başına uyarısız derlenir — çünkü derleyici aynı anda iki birimi
göremez, bu kursun ilk dersinden beri bilinen bir sınır.

Uyuşmazlık ancak iki birim bir araya gelince, yani bağlama sırasında ortaya çıkabilir. Ama
klasik bir C bağlayıcısı bir adın hangi imzayla bildirildiğini değil, yalnız adın var olup
olmadığını denetler — sembol tablosunda `kimlik` adını arar, bulur, adresi eşler. İmzanın
uyuşup uyuşmadığı çoğu zaman bağlayıcının da göremediği bir şeydir; hata orada da yakalanmaz,
tanımsız davranışa düşer. Çekirdekte durum böyle değildir: `asama.h`deki bildirim ile
`secim.c`deki tanım birebir örtüşüyor ve bu dersteki iki koşum da bunu zaten kanıtladı — hem
`kimlik` çağrısı hem `Konum` yapısı beklenen değerleri verdi. Başlık dosyası bu tutarlılığı
sağlamaz, yalnız onu **elle sağlanmışsa** taşır.

Bu sınır, önceki dersin ölçtüğü "aşamalar geri dönmez" bulgusunun bir başka görünümü. Ön
işlemci bir başlığı kopyalarken metnin doğru olup olmadığını sormaz, yalnız kopyalar.
Derleyici kopyalanan metni kendi biriminin içinde tutarlı bulduğu sürece bir şey söylemez.
Uyumsuzluk, iki birim ancak bağlanınca — ve o zaman bile yalnız bazı durumlarda — görünür
hale gelir. Bu yüzden bir başlık dosyasının doğruluğu, dilin denetlediği bir şey değil,
onu yazanın kendi disipliniyle koruduğu bir şeydir: bildirim değişince tanım da değişmeli,
ikisi ayrı dosyalarda dursa bile.

Bu disiplinin nasıl korunacağı ayrı bir konu — derleme sistemleri ve bağımlılık takibi
sonraki müfredatların konusu — ama bu dersin sınırı şunu söylüyor: `asama.h` var olduğu
için `kimlik`in imzası otomatik olarak doğru kalmıyor, yalnız **tek bir yerden**
değiştirilebiliyor. Başlık dosyasının asıl kazandırdığı şey doğruluk garantisi değil, bu
tekillik; bir bildirim değiştiğinde onu kullanan bütün birimlerin aynı metni görmesi.

#### Özet

- Bir ad kaynakta iki ayrı biçimde görünür: bildirim (declaration) yalnız var olduğunu
  söyler, tanım (definition) gövdesini ya da yerini verir.
- Bir bildirim birden çok birime kopyalanabilir; bir tanım yalnız bir birimde bulunabilir.
- Başlık dosyası bir birim değildir; yalnız metin taşır ve onu dahil eden her birime ayrı
  ayrı kopyalanır.
- Çifte içerme koruması (`#ifndef` / `#define`) aynı başlığın bir birime birden fazla
  yoldan girip bir tanımı iki kez kopyalamasını engeller.
- Başlık dosyası bir sözleşme değildir: bildirim ile tanım uyuşmazsa derleyici bunu
  göremez, çünkü tek seferde tek birime bakar; bağlayıcı da yalnız adı arar, imzayı değil.
- Bir başlığa tanım değil bildirim konması bir üsluptan çok bir zorunluluktur: aynı tanım
  onu dahil eden her birime kopyalanırsa, bu birimler bağlanırken çakışma doğar.

#### Sonraki Adım

Bu ders `baslik metni` satırının arkasındaki mekanizmayı açtı: ön işlemci bir dosyayı
kopyalıyor, ama kaynağın geri kalanı bu kopyalamanın yapıldığını hiçbir şekilde bilmiyor.
Kaynakta duran `#include "asama.h"` satırı ile derleyicinin gördüğü genişletilmiş metin
arasındaki fark, ön işlemcinin bütün kararlarının ortak özelliği: hiçbiri kaynakta iz
bırakmaz, yalnız sonucuyla var olur. Sıradaki ders ön işlemcinin geri kalan üç kararına
dönüyor — makro değeri, koşullu gövde ve ad birleştirme — ve her birinin metin düzeyinde
neden derleyiciden tamamen bağımsız çalıştığını, önceki dersten daha derin bir bakışla
gösteriyor.

### Ön İşlemci

Source: https://academia.sh/tr/kurslar/c-temelleri/on-islemci

Ön işlemcinin dört farklı yönergesi — makro değeri, koşullu gövde, ad birleştirme, içerme — aslında tek bir işin dört görünümüdür: metin ikamesi. Makro tip bilmediği için aynı ifadeyi iki farklı tiple doğru sonuç verecek şekilde üretir; ama argümanını birden çok kez yerine koyabildiği için yan etkili bir çağrıyı gerçek bir işlevden farklı sayıda çalıştırabilir.

Önceki ders `baslik metni` satırının arkasındaki mekanizmayı açtı ve ön işlemcinin bir
dosyayı kopyaladığını, ama kaynağın geri kalanının bu kopyalamadan habersiz kaldığını
gösterdi. Bu ders aynı gözlemi ön işlemcinin öteki üç kararına — makro değeri, koşullu
gövde, ad birleştirme — taşıyor ve dördünün de aslında tek bir işin görünümü olduğunu ortaya
koyuyor: kaynaktaki bir metni, derleyici hiç haberdar olmadan, başka bir metinle değiştirmek.

Bu dört karar kursun ilk dersinden beri tek tek görüldü: `KAT` metni, `#ifdef AYRINTILI`
seçimi, `BIRLESTIR(topla, _uc)` adı, `asama.h`nin kopyalanması. O derste dördü de yan yana,
tek bir koşumun parçası olarak geçti. Bu ders her birine ayrı ayrı döner ve ortak paydayı
gösterir: dördü de derleyici hiç devreye girmeden, salt metin üzerinde çalışır.

#### Dördü de Metin İkamesi

Ön işlemcinin bütün yönergeleri tek bir işi yapar: kaynaktaki bir metni başka bir metinle
değiştirmek.

| Yönerge | Neyi neyle değiştirir |
|---|---|
| `#define AD deger` | `AD` geçtiği her yeri `deger` ile |
| `#define AD(x) govde` | `AD(gercek)` çağrısını `govde` içindeki `x`ler `gercek` ile değiştirilmiş haliyle |
| `a##b` | İki token'ı tek bir yeni token ile |
| `#ifdef` / `#if` | Bir kod bloğunu ya kendisiyle ya da boş metinle |
| `#include "d.h"` | Bir satırı, dosyanın tüm içeriğiyle |

Beşi de aynı sınıfa girer, çünkü beşi de derleyici devreye girmeden, kaynağın metnini
değiştirir. Aralarındaki fark yalnız neyi neyle değiştirdikleridir — bir ad mı, bir çağrı mı,
iki token mı, bir kod bloğu mu, yoksa bir dosyanın tamamı mı.

Bu ortak paydanın bir sonucu var: ön işlemcinin ürettiği metin, derleyici için kaynaktan
elle yazılmış metinden ayırt edilemez. Derleyici bir satırın bir makrodan mı geldiğini, bir
başlıktan mı kopyalandığını, yoksa doğrudan mı yazıldığını bilmez ve bilmesine gerek de
yoktur — önündeki metin ne ise, onu derler. Bu görünmezlik hem ön işlemcinin gücünün hem
sınırlarının kaynağı: bir yandan kaynağı programcının elle yazmayacağı kadar tekrarlı
biçimlerde üretebiliyor, öte yandan ürettiği metnin doğruluğunu kendisi denetlemiyor.

Tablodaki beşinci satır — `#include` — önceki dersin bütün konusuydu, ama orada bir
paylaşım aracı olarak görüldü. Burada aynı yönergeye ikame ailesinin bir üyesi olarak
bakılıyor: `#include "asama.h"` satırı da tıpkı bir makro çağrısı gibi, kendi yerine başka
bir metni koyar. Aradaki tek fark ölçek — bir makro birkaç token'ı değiştirirken, bir
`#include` bütün bir dosyayı değiştirir. Ön işlemcinin gözünden ikisi arasında bir fark
yoktur; ikisi de aynı mekanizmanın, metni okuyup başka bir metinle değiştirmenin, farklı
boyutlardaki uygulamalarıdır.

#### Makro Tip Bilmez

Bir makro parametre alsa bile bir işlev değildir; parametre burada bir tip bildirmez, yalnız
gövde içinde nereye yapıştırılacağını gösteren bir yer tutucudur. Bu yüzden aynı makro,
farklı tiplerdeki argümanlarla doğru sonuç üretebilir. Gerçek bir C işlevi böyle davranamaz:
bir işlevin her parametresi tek bir tiple bildirilir ve çağrıda verilen argüman o tipe
dönüştürülür — dönüşüm bazen sessiz, bazen bilgi kaybıyla olur. Makronun parametresi bu
dönüşümden tamamen bağışıktır, çünkü dönüştürülecek bir tip zaten yoktur.

```c
// hesap.c — ayni makro iki farkli tiple cagriliyor
#include <stdio.h>

#define KARE(x) ((x) * (x))

int main(void) {
    int tam = KARE(4);
    double kesirli = KARE(2.5);

    printf("tam: %d\n", tam);
    printf("kesirli: %.2f\n", kesirli);

    return 0;
}
```

```
tam: 16
kesirli: 6.25
```

**DD9.** `KARE(4)` ön işlemcide `((4) * (4))` metnine, `KARE(2.5)` ise `((2.5) * (2.5))`
metnine dönüşüyor — ikisi de aynı gövdeden, `x`in yerine konan tek token değişerek üretildi.
Ön işlemci bu değişimi yaparken `4`ün bir tam sayı, `2.5`in bir kayan noktalı sayı olduğunu
hiç bilmiyor; onun gördüğü yalnız iki ayrı metin parçası. Sonucun `int` mi `double` mı
olacağına derleyici karar veriyor — ama bu karar, makronun genişlemesinden **sonra**, tıpkı
kaynağa elle `((4) * (4))` ve `((2.5) * (2.5))` yazılmış gibi veriliyor.

Bu, `KARE`in neden bir tip parametresi almadığını da açıklıyor: alamaz, çünkü zaten tip diye
bir kavramı yok. Aynı gövde her tipe uyuyor, çünkü hiçbir tipe özel yazılmadı — yalnız
metne yapıştırıldı.

Bir gerçek işlev aynı işi yapamaz — en azından tek bir tanımla. `int kare(int x)` yazılırsa
yalnız `int` argümanlarla çalışır; `KARE(2.5)`e benzer bir çağrı, `2.5`i sessizce `int`e
kesip `2`nin karesini, `4`ü verirdi. `KARE` makrosunun `int` ve `double` ile aynı gövdeyle
doğru sonuç vermesi bu yüzden bir kolaylık değil, tipin ön işlemci aşamasında hiç var
olmamasının doğrudan sonucu. Aynı esneklik bir bedel taşır: makro, argümanına hangi tipin
geleceğini denetleyemez, çünkü denetleyecek bir tip sistemi yoktur — `KARE("metin")` yazılsa
bile ön işlemci bunu reddetmez, yalnız `(("metin") * ("metin"))` metnini üretir ve
sorunu derleyiciye devreder.

#### Koşullu Gövde: Bir Adım İleri

Önceki dersler koşullu gövdeyi yalnız bir adın **tanımlı olup olmadığına** (`#ifdef`)
bakarak seçti. `#if` bundan bir adım ilerisini yapar: bir tam sayı sabit ifadesinin
**değerini** ön işlemcide hesaplar. Bu, ön işlemcinin tamamen boş bir kavrayışa sahip
olmadığını gösteriyor — metni okurken bazı basit hesapları da yapabiliyor, ama bu hesap
derleyicinin zengin tip sisteminden çok daha dar bir aritmetiktir.

```c
// esik.c — #if bir tam sayi sabit ifadesini on islemcide degerlendirir
#include <stdio.h>

#define ESIK 5

#if ESIK > 3
static const char *sonuc = "esik 3'ten buyuk";
#else
static const char *sonuc = "esik 3'ten kucuk ya da esit";
#endif

int main(void) {
    printf("%s\n", sonuc);
    return 0;
}
```

```
esik 3'ten buyuk
```

**DD10.** `#if ESIK > 3` derleyicinin `>` işlecini kullandığı yerle aynı sözdizimini taşısa
da, aynı hesap makinesini kullanmaz. Ön işlemcinin kendi aritmetiği vardır: bütün tam sayılar
tek bir geniş tipe genişletilir, kayan noktalı sayı hiç yoktur ve tanımsız bir ad `0` olarak
okunur. `ESIK`in değeri önce `5` metnine açılır, sonra `5 > 3` ön işlemcinin kendi
aritmetiğiyle hesaplanır ve sonuç `dogru` ya da `yanlis`a indirgenir. Derleyici bu hesabı hiç
görmez; onun önüne yalnız seçilen dal — burada `static const char *sonuc = "esik 3'ten
buyuk";` satırı — gelir.

Bu ayrım `#ifdef` ile `#if` arasındaki farkı da netleştiriyor. `#ifdef AD`, `AD`in tanımlı
olup olmadığına bakar — değerine hiç bakmaz; `AD` `0` olarak tanımlansa bile `#ifdef AD`
doğrudur. `#if AD`, tam tersine, `AD`in **değerini** ön işlemcinin kendi aritmetiğiyle
hesaplar; `AD` `0`sa `#if AD` yanlıştır. Kaynakta bu iki yönergeyi karıştırmak, tanımlı ama
sıfır bir sabitle çalışırken sessizce yanlış dalı seçtirebilir — bu ders bu iki yönergeyi
birbirinin yerine kullanmaz, ama aradaki farkı bilerek seçer.

`#if`in kendi aritmetiği sayesinde yapabildiği bir şey daha var: birden çok koşulu `&&`,
`||` ve karşılaştırma işleçleriyle tek bir satırda birleştirmek, ya da `#elif` ile art arda
sıralanan alternatiflerden ilk doğru olanı seçmek. `#ifdef` bunu yapamaz — yalnız tek bir
adın tanımlı olup olmadığını sorabilir, tanımlı iki addan hangisinin daha büyük bir sayıya
karşılık geldiğini soramaz. Bu, iki yönergenin neden birbirinin yerini tutmadığını bir kez
daha gösteriyor: biri varlığı sorar, öbürü değeri hesaplar.

#### Ad Birleştirme: Aynı Gövdeden Farklı Adlar

Önceki derste `##` tek bir örnekte görüldü: iki sabit token'ı (`topla` ve `_uc`) birleştirip
tek bir ad üretti. Asıl gücü orada görünmedi, çünkü o çağrı yalnız bir kezdi. `##`nin gerçek
gücü, aynı makro gövdesinin farklı çağrılarda farklı adlar üretebilmesidir — bir makro
gövdesi, kaynakta yalnız bir kez yazılıp defalarca çağrılarak, her seferinde yeni ve
birbirinden bağımsız bir ada dönüşebilir.

```c
// uret.c — ayni makro govdesi iki farkli cagriyla iki farkli isleve donusuyor
#include <stdio.h>

#define ISLEV_URET(ad) int uret_##ad(int x) { return x + 1; }

ISLEV_URET(a)
ISLEV_URET(b)

int main(void) {
    printf("uret_a(5) = %d\n", uret_a(5));
    printf("uret_b(5) = %d\n", uret_b(5));
    return 0;
}
```

```
uret_a(5) = 6
uret_b(5) = 6
```

**DD11.** `ISLEV_URET(a)` satırı ön işlemcide `int uret_a(int x) { return x + 1; }` metnine,
`ISLEV_URET(b)` satırı ise `int uret_b(int x) { return x + 1; }` metnine dönüşüyor. Derleyici
`ISLEV_URET` diye bir ad hiç görmez; onun gördüğü kaynakta iki ayrı işlev tanımı var, ikisi de
elle yazılmış gibi duruyor. Aynı makro gövdesi, argümanı değiştirilerek istenildiği kadar
farklı işlev üretebilir — ama her üretilen ad, gövdenin geri kalanıyla birlikte tek bir
metin parçası olarak derleyiciye gider; ikisi arasında hiçbir bağ, hiçbir "aynı makrodan
geldi" bilgisi taşınmaz.

Kursun ilk dersindeki `BIRLESTIR(topla, _uc)` de aynı işlemi yapıyordu, ama tek bir çağrıyla.
Buradaki fark, aynı üretici gövdenin **birden çok kez** çağrılabilmesi — `ISLEV_URET` iki
kez kullanıldı ve iki farklı işlev doğdu. Kaynağa üçüncü bir `ISLEV_URET(c)` satırı
eklenseydi, derleyici üçüncü bir işlev daha görürdü; ad birleştirmenin ürettiği metin
miktarı, makronun kaç kez çağrıldığına bağlıdır, kaynağın uzunluğuna değil.

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

Bu dersin dört bölümü de makronun tip bilmediğini, koşullu gövdenin kendi aritmetiğiyle
karar verdiğini, ad birleştirmenin yeni tanımlayıcılar üretebildiğini gösterdi — üçü de
ön işlemcinin **gücünü** anlatıyordu. Bu son bölüm aynı gücün bir bedelini gösteriyor.
Makro bir işlev değildir; argümanı **birden çok kez** yerine konabilir. `KARE(x)` gövdesinde
`x` iki kez geçiyor — bu, argüman yan etkili bir ifadeyse iki kez çalıştırılacağı anlamına
gelir. Aynı işi yapan gerçek bir işlevde argüman her zaman **bir kez** hesaplanır.

```c
// karsilastir.c — makro arguman iki kez, islev arguman bir kez calistiriyor
#include <stdio.h>

#define KARE(x) ((x) * (x))

static int sayac = 0;

static int artir(void) {
    sayac++;
    return sayac;
}

static int kare(int x) {
    return x * x;
}

int main(void) {
    sayac = 0;
    int makro_sonuc = KARE(artir());
    int makro_sayac = sayac;

    sayac = 0;
    int islev_sonuc = kare(artir());
    int islev_sayac = sayac;

    printf("makro: sonuc %d, sayac %d kez arttirildi\n", makro_sonuc, makro_sayac);
    printf("islev: sonuc %d, sayac %d kez arttirildi\n", islev_sonuc, islev_sayac);

    return 0;
}
```

```
makro: sonuc 2, sayac 2 kez arttirildi
islev: sonuc 1, sayac 1 kez arttirildi
```

**DD12.** `KARE(artir())` ön işlemcide `((artir()) * (artir()))` metnine dönüşüyor — kaynakta
`artir()` çağrısı artık **iki kez** yazılı. Program çalışırken bu iki çağrı da gerçekten
yapılıyor: `sayac` sıfırdan başlayıp önce `1`e, sonra `2`ye çıkıyor, çarpım `1 * 2 = 2`
veriyor. Hangi çağrının önce çalıştığı derleyiciye bağlıdır ve bu ders onu hiç iddia etmiyor
— iddia edilen tek şey, iki çağrının da gerçekleştiği ve `sayac`ın iki kez arttığıdır. `kare`
çağrısında ise `artir()` yalnız bir kez yazılı ve yalnız bir kez çalışıyor: argüman önce
hesaplanır, sonucu `kare`ye bir kez geçirilir, `x` gövde içinde ne kadar geçerse geçsin
artık sabit bir değerdir.

Bu fark, `KARE` gibi bir makroyu yan etkili bir argümanla çağırmanın neden riskli olduğunu
gösteriyor: makro yazarı argümanın kaç kez kullanılacağını gövdeye bakarak bilir, ama
çağıran taraf bunu görmeden, sıradan bir işlevmiş gibi çağırabilir. `KARE`in adı ve
kullanım biçimi bir işlevinkiyle aynı, ama sözleşmesi farklıdır — argüman yan etkisiz bir
ifade olmalıdır.

`KARE(x)` gövdesindeki `(x)` çevresindeki parantezlerin bu riskle bir ilgisi yok; onlar
ayrı bir sorunu çözüyor. Parantezsiz bir gövde (`x * x`) `KARE(1 + 2)`yi `1 + 2 * 1 + 2`
metnine açardı; çarpma toplamadan önce işlendiği için bu ifade `1 + 2 + 2 = 5` verir,
oysa beklenen `3`ün karesi, yani `9`dur — çarpma önceliği, toplamanın parçalarını
kendine argüman sanır. Parantezli gövde bunu önler: `((1 + 2) * (1 + 2))` önce iki parantezi
kendi bütünlüğü içinde değerlendirir, sonra çarpar, sonuç doğru biçimde `9` çıkar. Ama bu
parantezler argümanın kaç kez **yazıldığını** değiştirmez — `((1 + 2) * (1 + 2))` hâlâ
`1 + 2`yi iki kez taşıyor. İki sorun da aynı kökten gelir — argüman metne yerine konurken
çevresiyle etkileşir — ama çözümleri ayrıdır: parantez öncelik sırasını korur, argümanı
yan etkisiz tutmaksa çağıranın sorumluluğudur.

#### Özet

- Ön işlemcinin bütün yönergeleri tek bir işi yapar: kaynaktaki bir metni başka bir metinle
  değiştirmek; makro değeri, koşullu gövde, ad birleştirme ve içerme bunun dört görünümüdür.
- Bir makro tip bilmez: aynı gövde, farklı tiplerdeki argümanlarla, ön işlemci hiç
  değişmeden, doğru sonuç üretebilir.
- `#if` bir tam sayı sabit ifadesini ön işlemcinin kendi aritmetiğiyle hesaplar; bu aritmetik
  derleyicinin tip sistemiyle aynı değildir.
- Aynı makro gövdesi, ad birleştirmeyle farklı çağrılarda farklı adlar üretebilir; derleyici
  bu adların ortak bir kaynaktan geldiğini bilmez.
- Bir makro argümanını birden çok kez yerine koyabilir; yan etkili bir argüman bu yüzden
  gerçek bir işlevden farklı sayıda çalışabilir.
- Bir makro gövdesindeki parantezler öncelik sırasını korur, ama argümanın kaç kez
  yazıldığını değiştirmez — iki ayrı sorunun iki ayrı çözümüdür.

#### Sonraki Adım

Bu dört ders boyunca hiçbir kod bloğu bir uyarı bırakmadı — ama bu bir tesadüf değil, elle
sağlanan bir tutarlılıktı: her makro dikkatle parantezlendi, her koşullu dal tutarlı bir
tipte tutuldu, her ad birleştirme geçerli bir tanımlayıcıyla sonuçlandı. Ön işlemci bu
tutarlılığın hiçbirini denetlemez; onu denetleyen, kendisinden bir sonraki aşamadır.
Sıradaki ders bu denetimin kendisine bakıyor: bir uyarının hangi aşamada doğduğu, bir
derleyici uyarı bayrağının neyi görünür kıldığı ve standardın hangi davranışı garanti
edip hangisini uygulamaya bıraktığı. Bu konunun son dersi burada bu kursun açılışında
sorulan soruya döner: dört aşamanın her biri kendi payına düşen hatayı nasıl görünür
kılar.

### Derleyici Uyarıları ve Standartlar

Source: https://academia.sh/tr/kurslar/c-temelleri/derleyici-uyarilari-ve-standartlar

Bir uyarı yalnız derleyici aşamasında doğabilir, çünkü yalnız o aşama tek bir birimin içindeki tipleri ve ifadeleri görür. Standardın garanti ettiği ile uygulamaya bıraktığı yan yana konuyor — sizeof(char) == 1 garantidir, öteki boyutlar değildir. Uyarısız derlenen bir programın doğru olduğu da söylenemez; bunu göstermek için uyarı basmayan ama yanlış bir sonuç üreten bir hesap koşturuluyor.

Önceki ders ön işlemcinin dört kararını derinleştirdi ve kapanışta bir gözlemi öne çıkardı:
bu dört ders boyunca hiçbir kod bloğu bir uyarı bırakmadı, ama bu tesadüf değil, elle
sağlanan bir tutarlılıktı. Bu ders o tutarlılığın nasıl denetlendiğine bakıyor — bir
uyarının hangi aşamada doğduğuna ve standardın neyi garanti edip neyi uygulamaya
bıraktığına. Kurs, açılışında dört aşamayı adlandırmıştı; bu son ders o dört adı bir kez
daha kullanıyor, ama bu kez bir "karar" değil bir "denetim" için: dört aşamadan yalnız
biri, kendi girdisinin sınırları içinde kalarak, kaynağın **muhtemelen** yanlış olduğunu
düşünebiliyor. Bu dersin ölçtüğü, o düşüncenin doğduğu yer ve o düşüncenin ne kadar
güvenilir olduğudur.

#### Uyarı Nerede Doğar

Bir uyarı yalnız derleyici aşamasında doğabilir. Ön işlemci tip bilmediği için bir tipin
yanlış kullanıldığını fark edemez — onun elinde yalnız metin var. Bağlayıcı yalnız adları
eşler, bir ifadenin içeriğini hiç incelemez. Koşum ise kaynağın kendisini artık taşımıyor;
elindeki yalnız veri, uyaracağı bir şey yok. Derleyici ise tam olarak bunun tersini yapıyor
— tek bir birimin içindeki her ifadenin tipini, her değişkenin kullanımını, her denetim
yolunun hangi değeri döndürdüğünü görüyor ve bu görüşün sonucunda bir şeyin **şüpheli**
olduğunu düşünebiliyor.

Bu, Derleme Aşamaları dersindeki gözlemin bir uzantısı: dört aşamanın her biri yalnız kendi
girdisine bakıyordu ve bir hatanın nerede yakalanacağı da bu girdiyle sınırlıydı. Uyarı,
tam olarak derleyicinin kendi girdisinin sınırları içinde kalan bir denetimdir — yalnız
tek bir birime bakarak fark edebileceği bir şeyi işaretler. Bir hata birden çok birim
arasında yayılıyorsa (Başlık Dosyaları dersindeki bildirim/tanım uyuşmazlığı gibi),
derleyicinin bunu bir uyarıyla yakalaması da mümkün değildir — çünkü o hata, derleyicinin
girdisinin tamamen dışında kalır.

Şüpheli olmak yanlış olmakla aynı şey değildir. Bir uyarı tipik olarak şu kalıplardan birine
işaret eder: bir sabit değerin taşıyamayacağı kadar dar bir tipe doğrudan atandığı, bir
değişkenin tanımlandığı ama hiç kullanılmadığı, işaretli bir sayıyla işaretsiz bir sayının
doğrudan karşılaştırıldığı, ya da bir işlevin döndürmesi beklenen bir değeri bazı yollarda
hiç döndürmediği. Dördü de aynı özelliği taşır: kaynak **derlenebilir** durumdadır, ama
derleyici, tek bir birimin içindeki tipleri ve akışı görerek, kendi gördüğü kalıptan bir
risk çıkarmıştır.

Bu dört kalıbın ortak noktası, hiçbirinin kendi başına bir hata **olmamasıdır**. Kullanılmayan
bir değişken kaynağın çalışmasını engellemez; işaretli bir sayıyla işaretsiz bir sayının
karşılaştırılması geçerli bir C ifadesidir; bir işlevin bazı yollarda değer döndürmemesi
kaynağı derlenemez kılmaz. Derleyici bu kalıpların hiçbirini **reddetmiyor**, yalnız
işaretliyor — çünkü her biri, programcının aslında istemediği bir şeyin **belirtisi**
olabilir, ama olmak zorunda da değildir. Bu, uyarının neden isteğe bağlı bir bayrakla
(`-Wall`, `-Wextra` gibi) açılması gerektiğini de açıklıyor: standart, derleyicinin hangi
kalıpları şüpheli sayacağını belirlemez, yalnız programın hangi kalıplarla **derlenebilir**
kalacağını belirler.

Bu kursun bütün kod örnekleri bu dört kalıptan kaçındı, ama kaçınmak elle yapılan bir
seçimdi — dilin kendisi hiçbir örneği bu kalıplara girmekten alıkoymadı. Bu konudaki yirmi
bir kod bloğunun hepsi uyarısız kaldıysa, bunun nedeni derleyicinin cömert davranması
değil, her değişkenin kullanılması, her karşılaştırmanın aynı işaretteki tiplerle
yapılması, her işlevin her yolda bir değer döndürmesiydi.

Bu tutarlılığı sağlamak, kursun kalan bölümünde de aynı yükümlülük olarak kalacak. Bir
uyarı çıkardığında, o uyarı kaynaktaki tek bir birime, tek bir ifadeye işaret eder — kaynağı
düzeltmenin ilk adımı, uyarının hangi birime ait olduğunu bilmektir. Bu da uyarının neden
derleyici sınıfına bu kadar sıkı bağlı olduğunu bir kez daha doğruluyor: bir uyarı, ancak
onu doğuran birim yeniden derlenince kaybolur; başka hiçbir aşamanın onu susturmaya gücü
yetmez.

#### Ne Garanti, Ne Değil

C standardı (C17) bazı şeyleri kesin olarak garanti eder, bazılarını uygulamaya bırakır.
Aradaki fark koşturarak gösterilebilir — ama yalnız garanti edilen tarafı, çünkü garanti
edilmeyen bir sayı bu kursun hiçbir yerinde basılmaz.

Bu ayrım rastgele değil, dilin donanıma yakınlığının doğrudan sonucu. Dört ayrı donanım
dört ayrı tam sayı genişliği sunabilir; standart, dilin her donanımda çalışabilmesi için
kesin bir genişlik dayatmak yerine bir **alt sınır** dayatıyor ve gerisini derleyiciye,
dolayısıyla dolaylı olarak donanıma bırakıyor. `int`in bu makinede kaç bayt olduğu bu
yüzden dilin bir sözü değil, derleyicinin bir kararıdır — ve bu kurs boyunca hiçbir yerde
basılmayan bir gözlemdir.

```c
// garanti.c — standardin garanti ettigi ile birakmadigi yan yana
#include <limits.h>
#include <stdio.h>

int main(void) {
    printf("sizeof(char) == 1: %s\n", (sizeof(char) == 1) ? "dogru" : "yanlis");
    printf("CHAR_BIT >= 8: %s\n", (CHAR_BIT >= 8) ? "dogru" : "yanlis");
    printf("INT_MAX >= 32767: %s\n", (INT_MAX >= 32767) ? "dogru" : "yanlis");
    printf("LONG_MAX >= 2147483647: %s\n", (LONG_MAX >= 2147483647L) ? "dogru" : "yanlis");
    return 0;
}
```

```
sizeof(char) == 1: dogru
CHAR_BIT >= 8: dogru
INT_MAX >= 32767: dogru
LONG_MAX >= 2147483647: dogru
```

**DD13.** Dört satırın dördü de bir **karşılaştırmanın sonucunu** basıyor, hiçbiri ham bir
sayıyı basmıyor. `sizeof(char)`, `CHAR_BIT`, `INT_MAX`, `LONG_MAX`in kendi değerleri
uygulamaya göre değişebilir — standart yalnız bir **alt sınır** garanti eder, üst sınırı
serbest bırakır. `int`in gerçekte kaç bayt olduğu, `long`un gerçekte kaç bit taşıdığı bu
kursun hiçbir yerinde basılmadı ve basılmayacak; basılan yalnız "bu değer en az şu kadardır"
sorusunun `dogru`/`yanlis` yanıtı, çünkü bu yanıt standarda uygun her C17 derleyicisinde
aynıdır.

Bu dört satırın hepsi `dogru` çıktı, çünkü hepsi gerçekten garanti edilen alt sınırlar.
Standardın garanti **etmediği** şey ise satırlarda hiç görünmüyor: `sizeof(int)`in tam
değeri, `int`in `short`tan gerçekten daha geniş olup olmadığı, `char`ın imzasız mı imzalı
mı sayıldığı. Bunların hepsi uygulamaya bırakılmıştır ve bu yüzden bu kursun hiçbir
dersinde koşturulmadı.

Alt sınır ile üst sınır arasındaki bu fark, kursun daha önce gördüğü "garanti ile kesit"
ayrımına benziyor: bir başlık dosyası bir bildirimi paylaşabiliyordu, ama tanımın onunla
uyuşacağını garanti etmiyordu (Başlık Dosyaları dersi); burada da standart bir alt sınır
veriyor, ama gerçek değerin o sınırın neresinde duracağını garanti etmiyor. İkisinde de
aynı örüntü tekrar ediyor: dilin verdiği söz her zaman bir **asgari**dir, üstüne ne
eklendiği uygulamaya, yani derleyiciye ve donanıma kalır.

Bu dört satırın hiçbiri bir uyarı da doğurmadı ve bu tesadüf değil. `CHAR_BIT >= 8` gibi bir
karşılaştırma, iki sabit ifadenin karşılaştırılmasıdır ve derleyici bunu şüpheli bulacak bir
kalıp görmüyor — karşılaştırmanın iki tarafı da aynı geniş tam sayı sınıfına ait, işaretli
ile işaretsiz arasında bir karışıklık yok, bir değer daralmıyor. Standardın garanti ettiği
tarafı ölçmek bu yüzden hem doğru hem güvenli: ne ortama bağlı bir sayı sızdırıyor, ne de
derleyicinin şüphelendiği bir kalıba giriyor.

Bu ayrımın pratik bir sonucu var: bir kaynak, yalnız standardın **garanti ettiği** taraf
üzerine kurulduğunda, hangi derleyiciyle ve hangi donanımla derlendiğinden bağımsız olarak
aynı sonucu verir. `sizeof(char) == 1` her yerde `dogru`dur, çünkü standart bunu zaten
zorunlu kılmıştır. Ama bir kaynak `sizeof(int) == 4` gibi bir varsayıma dayansaydı, bu
varsayım bazı uygulamalarda doğru, bazılarında yanlış çıkabilirdi — ve derleyici bu
varsayımı kaynakta göremeyeceği için, yanlış çıktığı yerde de bir uyarı basmazdı. Yalnız
garanti edilen tarafa dayanan bir kaynak, hangi derleyiciyle ve hangi donanımla
derlendiğinden bağımsız olarak doğruluğunu korur; garanti edilmeyen bir tarafa dayanan bir
kaynak ise, derleyici hiçbir uyarı basmasa bile, farklı bir donanımda sessizce yanlış
sonuç verebilir.

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

Bir uyarı bir hata değildir; derleyici uyarı bastıktan sonra bile programı derlemeye devam
eder ve program çalışır. Bunun aynadaki yansıması da doğrudur: uyarısız derlenen bir
programın doğru olduğu **söylenemez**. Bu iki cümle birlikte, uyarının ne olduğunu değil,
ne **olmadığını** tanımlıyor: bir uyarı ne bir hata kanıtıdır, ne yokluğu bir doğruluk
kanıtıdır — yalnız derleyicinin fark ettiği şeyler hakkında bir bilgidir, o kadar.
Aşağıdaki program hiçbir uyarı bırakmadan derlenir, ama ikinci satırı beklenmeyen bir
sonuç verir.

```c
// oran.c — uyarisiz derlenen bir program dogru oldugunu garanti etmez
#include <stdio.h>

int main(void) {
    int parca = 2;
    int toplam = 5;

    int tamsayi_oran = parca / toplam * 100;
    double kayan_oran = (double)parca / toplam * 100;

    printf("tam sayiyla hesaplanan oran: %d\n", tamsayi_oran);
    printf("kayan noktayla hesaplanan oran: %.1f\n", kayan_oran);

    return 0;
}
```

```
tam sayiyla hesaplanan oran: 0
kayan noktayla hesaplanan oran: 40.0
```

**DD14.** İki satır da aynı iki sayıdan (`2` ve `5`) yola çıkıyor ve ikisi de derleyiciden
tek bir uyarı almadı. Ama `tamsayi_oran` `0` verdi, çünkü `parca / toplam` iki `int`
arasında yapıldı ve `2 / 5` sıfıra doğru yuvarlanarak `0` verdi — çarpma `100`le ondan sonra
geldi ve zaten sıfır olan bir sayıyı yüz katına çıkarmak yine sıfır verdi. `kayan_oran` ise
`parca`yı önce `double`a çevirdiği için bölme kayan noktalı aritmetikte yapıldı ve doğru
oranı, `40.0`ı verdi. Derleyici bu ikisinden hiçbirini "yanlış" bulmuyor, çünkü ikisi de
kendi tipiyle tutarlı: `int / int` işleminin sonucunun `int` olması C'nin bir kuralı, bir
hata değil — bu kursun ikinci dersinde `7 / 2` ile `7 / 2.0` arasındaki fark olarak zaten
görüldü.

Kaynakta `tamsayi_oran`ı düzeltmenin yolu, tıpkı `kayan_oran`da olduğu gibi, bölmeden önce
taraflardan birini `double`a çevirmektir — programın çalıştırdığı işlem aynı kalır, yalnız
hangi aritmetiğin kullanılacağı kaynakta açıkça belirtilir. Derleyici bu düzeltmeyi
öneremez, çünkü önermek için "programcı burada bir oran istiyordu" bilgisine ihtiyaç duyar
ve bu bilgi hiçbir yerde yazılı değildir. Derleyicinin görebildiği tek şey, iki ifadenin de
kendi tipiyle tutarlı olduğudur.

Bu, dilin verdiği bir güvenceyle programcının verdiği bir kararı birbirinden ayırıyor. Dil
`int / int`in `int` vereceğini garanti ediyor ve bunu tutarlı biçimde uyguluyor — derleyici
burada hiçbir şeyi ihlal etmiyor. Yanlış olan, iki tam sayının bölümünün bir **oran**
taşıyacağını varsaymak; bu varsayım kaynakta hiçbir yerde yazılı değil, yalnız programcının
zihnindeydi. Derleyici zihinde olanı göremez, yalnız kaynakta yazılanı görür — ve kaynakta
yazılan, tamamen tutarlı, tamamen uyarısız bir `int` bölmesiydi.

Bu, dört ders boyunca kurulan bir örüntünün son halkası: her aşama yalnız kendi girdisine
bakar ve kendi girdisinde tutarlı olan her şeyi kabul eder. Ön işlemci metni tutarlı buldu,
derleyici tipleri tutarlı buldu — ama tutarlılık doğrulukla aynı şey değildir. `tamsayi_oran`
değişkeni derleyicinin gözünden kusursuzdur; bir programcının gözünden ise muhtemelen bir
hatadır. Bu ikisi arasındaki boşluk, hiçbir aşamanın dolduramayacağı bir boşluktur — onu
yalnızca kaynağı yazan doldurabilir.

Bu, beş ders boyunca kurulan çerçevenin son dersi için de doğru olan bir sonuç: dört
aşamanın hiçbiri, kaynağın **doğru** olduğunu kanıtlamaz — yalnız kendi aşamasına özgü bir
tutarlılığı denetler. Ön işlemci metnin biçimini denetler, derleyici tipleri ve akışı
denetler, bağlayıcı adları denetler, koşum ise yalnız verdiği veriyle çalışır. Dördü birden
geçildiğinde elde edilen şey, çalışan bir programdır — ama çalışan bir programın **istenen**
programla aynı olduğunu söyleyen hiçbir aşama yoktur.

#### Özet

- Bir uyarı yalnız derleyici aşamasında doğabilir; yalnız bu aşama tek bir birimin içindeki
  tipleri ve ifadeleri görüp bir kalıbı şüpheli bulabilir.
- Bir uyarı bir hatayla aynı şey değildir: program uyarıyla birlikte de derlenir ve çalışır.
- Standart bazı şeyleri kesin garanti eder (`sizeof(char) == 1`, `CHAR_BIT >= 8`), bazılarını
  uygulamaya bırakır (gerçek tip boyutları); bu kurs yalnız garanti edileni bastı.
- Uyarısız derlenen bir program doğru olduğunu göstermez: `int / int` bölmesi dilin bir
  kuralıdır, ama sonucu bir oran sanmak programcının kaynakta yazmadığı bir varsayımdır.
- Standardın verdiği söz her zaman bir asgaridir; üstüne ne eklendiği derleyiciye ve
  donanıma kalır, bu yüzden bu kurs boyunca yalnız asgari, garanti edilen taraf koşturuldu.
- Dört aşamanın hiçbiri kaynağın doğru olduğunu kanıtlamaz; her biri yalnız kendi aşamasına
  özgü bir tutarlılığı denetler, çalışan bir programla istenen program arasındaki farkı
  yakalamak dilin değil kaynağı yazanın işidir.

#### Sonraki Adım

Bu, Dil ve Derleme konusunun son dersidir. Beş ders aynı çekirdekten, aynı dört sınıftan
geçti ve her biri ayrı bir köşeyi aydınlattı. C'nin Konumu çekirdeği tam koşturdu ve on dört
gözlemin 4/4/3/3 dağılımıyla dört aşamanın adını koydu. Derleme Aşamaları aynı dört sınıfı
küçük bir kesitle yan yana koşturup her birinin hangi girdiye baktığını gösterdi ve
aşamaların geri dönüşü olmayan bir sırayla çalıştığını kanıtladı. Başlık Dosyaları bildirim
ile tanımı ayırdı ve bir başlığın aynı bildirimi birden çok birime nasıl taşıdığını, çifte
içerme korumasının neyi engellediğini gösterdi. Ön İşlemci dört ayrı yönergenin aslında tek
bir işin — metin ikamesinin — görünümü olduğunu ortaya koydu ve bir makronun argümanını
birden çok kez yerine koyabildiğini kanıtladı. Bu son ders beşinci köşeye baktı: bir
uyarının yalnız derleyicide doğabildiğini ve uyarısız derlenmenin doğruluk garantisi
olmadığını.

Beş dersin ortak paydası, hiçbirinin öbürünün ölçtüğü şeyi tekrar ölçmemesiydi — her biri
aynı dört adı kullandı, ama her biri kendi sorusunu sordu. Bu ortak payda kursun geri
kalanında da sürecek: Temel Sözdizim konusunun sekiz dersi de aynı dört sınıfı kullanacak,
ama her biri C'nin farklı bir köşesine — tiplere, değişkenlere, işleçlere, denetim akışına,
fonksiyonlara, dizilere, kayıt tiplerine, girdi ve çıktıya — bakacak.

Temel Sözdizim konusu bu çerçeveyi değiştirmiyor, ona C'nin kendi tiplerini, değişkenlerini
ve işleçlerini ekliyor; ilk soru, bir tipin taşıdığı değerlerin hangi aşamada karara
bağlandığıdır.

## Temel Sözdizim

Tiplerin taşıdığı değer aralıkları ve dönüşüm kuralları, bir değişkenin kapsamı ile ömrünün ayrı sorular olması, işleçlerin tipe göre anlam değiştirmesi, denetim akışı biçimleri, fonksiyon bildirimi ile tanımının ayrılması, dizinin ve dizginin aynı düzeneğe dayanması, kayıt tiplerinin bellek düzeni, ve girdi çıktının biçim dizeleriyle yürümesi.

### Veri Tipleri

Source: https://academia.sh/tr/kurslar/c-temelleri/veri-tipleri

Bir tipin taşıyabileceği değer aralığının, işaretli-işaretsiz ayrımının ve tam sayı yükseltmesinin derleyicide karara bağlanması; sizeof(char) == 1 dışında hiçbir boyutun basılmaması.

Derleyici Uyarıları ve Standartlar dersi, bir uyarının hangi aşamada doğduğunu ve
standardın neyi garanti edip neyi garanti etmediğini ölçtü: `sizeof(char) == 1` bir
garantiydi, öteki boyutlar değildi. Bu ders o ayrımın üstüne kuruluyor ve daha temel bir
soruyu ölçüyor: bir tip, taşıyabileceği değerlerin sınırını hangi aşamada belirliyor?

Yanıt derleyicidir. Bir kaynak dosyada `int` yazıldığında, o adın taşıyabileceği aralık
ne kaynağın kendisinde ne koşumda; birimin çevrildiği anda, hedef için seçilmiş temsille
birlikte karara bağlanır. **Bu ders M08/K05'te ölçülen "bir tipin hangi sınıf yolunda
bulunması gerektiği" sorusuyla da, M09/K05'te ölçülen "bir bilginin kaynağı" sorusuyla da
karışmaz** — ikisi de bu kursun dışında kalan eksenlerdi; burada ölçülen, bir tipin
taşıdığı değerin hangi aşamada karara bağlandığı ve o kararın sınırlarının nereden
geldiği. Programlama Temelleri kursu (M01/K02) değişken ve tip kavramını Python'la zaten
kurmuştu; orada bir adın hangi değerleri tutabileceği koşum sırasında değişebilirdi, bir
tipin sınırı da ayrıca sorulmazdı. C'de bu iki soru ayrılıyor: tipin kendisi bir sınır
taşıyor ve o sınır kaynakta yazılan tip adıyla birlikte derlemede sabitleniyor. Bir
değişken bildirildikten sonra o değişkenin taşıdığı tip, programın geri kalanı boyunca
değişmez; değer değişebilir, ama değerin hangi aralıkta ve hangi kuralla yorumlanacağını
belirleyen tip sabit kalır. Bu ders o sabitliğin ne anlama geldiğini, dört ayrı kararla —
aralık, yükseltme, işaret ve açık dönüşüm — ölçüyor.

Bu sınırın büyüklüğü hedefe göre değişir ve bu değişkenlik kasıtlıdır: dil, aynı tip
adının farklı donanımlarda farklı genişliklerle temsil edilmesine izin vererek her hedefte
verimli bir eşleşme kurulmasını sağlar. Ama bu esneklik, bir dersin ölçümlerini o hedefe
bağımlı kılmamalı — bir ölçüm burada koşulduğu makinede doğruysa, beş yıl sonra başka bir
hedefte de doğru kalmalı. Bu yüzden bu derste hiçbir gerçek boyut değeri yazdırılmıyor;
onun yerine iki tipin **göreli** ilişkisi ölçülüyor. Bir ilişkinin kendisi de hedeften
hedefe değişebilir, ama belirli bir hedef için derleyicinin verdiği karar sabittir ve
aynı ikili dosya her koşumda aynı ilişkiyi bildirir.

#### Boyut Birimi ve Karşılaştırmalı Sonuç

Bir tipin taşıdığı aralığın büyüklüğü hedefe bağlıdır ve bu kursta hiçbir yerde
basılmaz — istisna, standardın açıkça garanti ettiği tek satır: `sizeof(char)` her zaman
`1`dir, çünkü "bayt" ölçü birimi zaten `char` üzerinden tanımlanır. Aşağıdaki ölçüm bu
satırı ve iki tipin göreli büyüklüğünü mantıksal bir sonuçla — büyük mü küçük mü,
`1`/`0` — okuyor; hiçbir gerçek boyut değeri yazdırılmıyor.

```c
// asama.c — tipin tasidigi degerin derleyicide karara baglanmasi
#include <stdio.h>
#include <limits.h>

enum Renk { KIRMIZI = 1, YESIL = 2, MAVI = 4 };

static void gozle(const char *ad, int deger) {
    printf("%-34s%d\n", ad, deger);
}

int main(void) {
    gozle("tip boyutu birimi", (int)sizeof(char));
    gozle("numaralandirma sabiti", (int)MAVI);
    gozle("int char tan genis mi", (int)(sizeof(int) > sizeof(char)));
    gozle("long int tan genis mi", (int)(sizeof(long) > sizeof(int)));
    gozle("short int tan dar mi", (int)(sizeof(short) < sizeof(int)));

    char kucuk_a = 100, kucuk_b = 100;
    int yukseltilmis_toplam = kucuk_a + kucuk_b;
    gozle("yukseltilmis toplam", yukseltilmis_toplam);

    unsigned char tasan = UCHAR_MAX;
    tasan = (unsigned char)(tasan + 1);
    gozle("isaretsiz tasma sonucu", tasan);

    int eksi = -1;
    unsigned int esik = 1;
    gozle("isaretsize donunce kucuk mu", (int)((unsigned int)eksi < esik));

    return 0;
}
```

```
tip boyutu birimi                 1
numaralandirma sabiti             4
int char tan genis mi             1
long int tan genis mi             1
short int tan dar mi              1
yukseltilmis toplam               200
isaretsiz tasma sonucu            0
isaretsize donunce kucuk mu       0
```

`tip boyutu birimi` satırı standardın garantisini doğruluyor: `char` her zaman tek bayttır
ve bu, kursun ölçtüğü tek boyut değeri kalacak. Geri kalan üç karşılaştırma satırı ise
gerçek bir sayı vermiyor, yalnız bir ilişkinin sonucunu veriyor — hedefte `int` `char`tan
büyük mü, `long` `int`ten büyük mü, `short` `int`ten dar mı diye soruyor ve üçüne de `1`
(doğru) yanıtı geliyor. **TS1.** Bu karşılaştırmaların kendisi de derleyicide karara
bağlanıyor: her ilişki hedeften hedefe değişebilir, ama belirli bir birim çevrilirken
sabittir ve koşum boyunca değişmez — aynı ikili dosya her koşumda aynı `1` ya da `0`
değerini üretir.

Standart bu tiplerin göreli sırasını da zaten garanti eder: `char`, `short`, `int`,
`long` ve `long long` arasındaki genişlik hiçbir zaman geriye doğru olamaz — her biri
kendisinden öncekinin taşıyabildiği her değeri taşıyabilecek genişliktedir. Bu bir ölçüm
değil, dilin kendi belirtimidir; bu ders yalnız belirli bir hedefte bu sıranın **katı**
mı yoksa **eşit** mi gerçekleştiğini ölçüyor (burada üçü de katı çıktı). Bazı hedeflerde
`int` ve `long` aynı genişlikte olabilir — standart yalnız `long`ın `int`ten **dar
olamayacağını** garanti eder, kesinlikle **daha geniş** olacağını değil. Bu derste
ölçülen `1` değeri bu yüzden bu hedefe özgü bir gözlemdir, dilin kendisinin verdiği bir
söz değil.

#### Tam Sayı Yükseltmesi

`numaralandirma sabiti` satırı, Derleme Aşamaları dersinde tanıtılan `enum Renk`
sabitinin bir kesitidir: `MAVI` değeri `4`, kaynakta yazılan sabit ifadeden derlemede
üretiliyor. Aynı ölçümde `yukseltilmis toplam` satırı farklı bir kararı gösteriyor:
`kucuk_a` ve `kucuk_b` birer `char`, ama toplamları `char` sınırında değil `int`
sınırında hesaplanıyor. C standardı, `char` ve `short` gibi dar tipleri bir işleme
girmeden önce `int`e **yükseltir** — buna tam sayı yükseltmesi denir. `100 + 100`
işlemi bir `char` üzerinde yürütülseydi (tipik olarak `-128`–`127` aralığında) sonuç
sığmazdı; ama işlem `int` genişliğinde yürüdüğü için `200` sorunsuz çıkıyor. Bu
yükseltme kaynakta görünmez — hiçbir dönüştürme yazılmadı — ama derleyici her `char`
işlenenini işlemden önce `int`e taşıyor ve bu karar tamamen tiplerden geliyor, veriden
değil.

**TS2.** Yükseltme kuralı, işlenen değerin ne olduğuna bakmaksızın uygulanır: `kucuk_a`
ve `kucuk_b` `10` ve `20` olsaydı da toplam yine `int` genişliğinde hesaplanırdı, sonuç
`char` sınırları içinde kalsa bile. Derleyici, işlemi yürütmeden önce hangi genişlikte
çalışacağına yalnız işlenenlerin **tipine** bakarak karar veriyor; bu, işleçlerin veriye
göre değil tipe göre davrandığı ilkenin ilk örneği ve İşleçler dersinde derinleşecek.

Yükseltme yalnız aritmetik işleçlerle sınırlı değildir; bir işlev değişken sayıda
bağımsız değişken aldığında (`printf` bunun standart kitaplıktaki örneğidir) `char` ve
`short` bağımsız değişkenler de çağrı öncesinde `int`e yükseltilir. Buna **varsayılan
bağımsız değişken yükseltmesi** denir ve yalnız `...` kısmına düşen argümanlarda —
prototipin son bildirilen parametresinden sonrasında — işler. `gozle` bundan farklıdır:
prototipli ve sabit imzalı bir işlevdir, dolayısıyla `tasan` değişkeni `unsigned char`
olsa da değer, ikinci parametre `int` bildirildiği için sıradan bir atama dönüşümüyle
`int`e taşınır; `gozle` gövdesindeki `%d` de bu bildirilen tiple eşleşir. İki mekanizma
ayrıdır: biri yalnız değişken argümanlı çağrıların kuyruğunda, öbürü her prototipli
çağrıda işler. Bir dar tipi doğrudan uyuşmayan bir biçim belirleyiciyle yazdırmak
(`unsigned char` için `%hhu` yerine `%d` kullanmak gibi) `printf` tarafında çoğu durumda
varsayılan yükseltme sayesinde çalışır, ama bu kırılgan bir uyumdur ve biçim dizeleri
konusu Girdi/Çıktı dersinde ayrıca ele alınacak.

#### Açık Dönüşümle Sınırlama

Yükseltme örtük bir kuraldır — kaynakta hiçbir işaret bırakmadan işler. Ama kaynak yazarı
tam tersini de isteyebilir: geniş bir değeri kasıtlı olarak dar bir tipe sığdırmak. Bu,
açık bir dönüşümle (cast) yapılır ve kararın kendisi yine derlemede, tipin adı yazılırken
veriliyor.

```c
// daraltma.c — acik donusum, sinirlamayi kaynakta gorunur kilar
#include <stdio.h>

static void gozle(const char *ad, int deger) {
    printf("%-34s%d\n", ad, deger);
}

int main(void) {
    int genis_deger = 300;
    unsigned char daraltilmis = (unsigned char)genis_deger;
    gozle("acikca daraltilan deger", daraltilmis);

    int ikinci_deger = 600;
    unsigned char ikinci_daraltilmis = (unsigned char)ikinci_deger;
    gozle("ikinci acikca daraltilan deger", ikinci_daraltilmis);
    return 0;
}
```

```
acikca daraltilan deger           44
ikinci acikca daraltilan deger    88
```

`300` değeri sekiz bitlik işaretsiz bir tipe sığmıyor, ama dönüşüm yine de tanımlı: aynı
sarma kuralı burada da geçerli ve `300 mod 256 = 44` çıkıyor. `600` için de aynı kural
işliyor ve `88` veriyor. Bu, bir önceki bölümdeki taşmadan farklı bir olay değil, aynı
kuralın kaynakta **açıkça istenerek** tetiklenmiş hali. Örtük yükseltme kaynakta görünmezdi;
açık dönüşüm ise `(unsigned char)` yazımıyla görünür — okuyan kişi, bir daraltmanın
kasıtlı olduğunu kaynaktan anlayabilir. İki durumda da hangi kuralın uygulanacağı, işlenen
değere değil, hedef tipin işaretine bakılarak derlemede belirleniyor.

#### İşaretli-İşaretsiz Ayrımı ve Tanımlı Sarma

Bir tipin işaretli mi işaretsiz mi olduğu, taşıdığı aralığın nereden başladığını
belirler ve bu da derlemede karara bağlanan bir özelliktir. `isaretsiz tasma sonucu`
satırı, `unsigned char` tipindeki bir değişkenin üst sınırına (`UCHAR_MAX`) ulaştıktan
sonra bir artırılmasını ölçüyor: sonuç `0`. Bu, bir hata değil — standart, işaretsiz
tipler için taşmayı **tanımlı** kılar: sonuç, tipin temsil edebileceği değer sayısına göre
mod alınarak "sarar." `255 + 1`, sekiz bitlik işaretsiz bir tipte `256 mod 256 = 0` verir
ve bu her koşumda aynı sonucu üretir.

`isaretsize donunce kucuk mu` satırı işaretli-işaretsiz ayrımının başka bir yüzünü
gösteriyor: `-1` değerindeki işaretli bir tamsayı, işaretsiz bir tiple karşılaştırılmadan
önce işaretsize dönüştürülüyor. Bu dönüşüm de tanımlıdır — negatif bir değer işaretsize
dönüştürüldüğünde, tipin temsil edebileceği değer sayısı eklenerek büyük bir işaretsiz
sayıya taşınır. Sonuç olarak `-1`, `esik` değişkenindeki `1`den **küçük değil**, çok daha
büyük görünür ve karşılaştırma `0` (yanlış) döner. Kaynakta yazılan işaretler karşılaştırma
sırasında derleyici tarafından bu şekilde uzlaştırılır; hangi tarafın hangi tipe
dönüştürüleceği veriye değil, iki işlenenin **bildirilen** tipine bakılarak belirlenir.

Bu uzlaştırma kuralı, işaretsiz tiplerin neden sıklıkla sayaç ve boyut taşımak için
tercih edildiğini de açıklıyor: standart kitaplığın `sizeof` işleci `size_t` döndürür ve
bu tip işaretsizdir, çünkü negatif bir boyut kavramsal olarak anlamsızdır. Ama aynı seçim
bir tuzak da taşır. Bir döngü sınırını işaretsiz bir sayaçla geriye doğru yazmak —
`sayac`ı `0`dan aşağı indirmeye çalışmak — sayaç işaretsiz olduğu için hiçbir zaman
negatife düşmez, üst sınırdan sarar ve döngü koşulu hep doğru kalır. Bu durum tanımsız
değildir; sarma kuralı tam olarak yukarıdaki ölçümde görüldüğü gibi işler. Ama sonsuz bir
döngüye yol açacağı için bu ders bu senaryoyu koşturmuyor, yalnız işaretsiz sarmanın bir
sınır koşulunda nasıl **beklenmedik** bir sonuca dönüşebileceğini not ediyor —
davranışın kendisi tanımlı, sonucu yorumlamak kaynağı yazanın işi.

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

Taşma davranışı tipin işaretine bağlıdır ve bu bağımlılık simetrik değildir. İşaretsiz
tipler için taşma yukarıda ölçüldüğü gibi tanımlıdır ve sarma her zaman aynı kurala
uyar. İşaretli bir tipte taşma ise **tanımsız davranıştır**: `INT_MAX` değerindeki bir
`int`i bir artırmak, standardın hiçbir garanti vermediği bir duruma düşer — derleyici bu
durumu varsaymadığı için optimizasyonlar beklenmeyen sonuçlar üretebilir, program
çökebilir ya da hiçbir gözle görülür etki olmayabilir. Bu davranış kursun genel
kuralınca kod bloğunda **denenmiyor**.

Yanına konan tanımlı karşılık, yukarıdaki `unsigned char` ölçümüdür: aynı "üst sınırdan
bir öteye geçme" senaryosu, işaretsiz bir tiple kurulduğunda tam olarak öngörülebilir bir
sonuç veriyor. İki senaryo da aynı soruyu soruyor — tip sınırının ötesine geçilince ne
olur — ama yanıt yalnız işaretin kendisine bağlı: işaretsizde bir aritmetik kural,
işaretlide bir boşluk. Bir tipi işaretli ya da işaretsiz seçmek, bu yüzden yalnız hangi
değerlerin temsil edilebileceğini değil, sınırın aşılması durumunda programın hangi
kategoriye düştüğünü de belirleyen bir karardır ve bu karar tamamen derlemede, kaynakta
yazılan tip adıyla birlikte veriliyor.

Bu asimetrinin kaynağı, iki temsilin donanımdaki karşılığında yatar. İşaretsiz bir tip,
bitlerini doğrudan bir sayının ikili gösterimi olarak okur; sarma, o gösterimin sınırlı
genişliğinin doğal bir sonucudur ve her hedefte aynı biçimde işler. İşaretli bir tipte ise
negatif değerlerin temsili hedefe bağlı olabilir ve taşma durumunda hangi sonucun doğacağını
her temsil için garanti etmek, derleyicinin üst sınıra yakın değerler için yapabileceği
bazı optimizasyonları kısıtlardı. Bu yüzden dil, işaretli taşmayı tanımlamak yerine
tanımsız bırakmayı seçti — davranışı sabitlemek yerine derleyiciye "bu asla olmayacak"
varsayımıyla çalışma özgürlüğü tanıdı.

Bu asimetrinin pratik sonucu, işaretli ve işaretsiz tipler arasında seçim yaparken
yalnız "hangi değerler gerekli" sorusunun değil, "sınır aşılırsa ne olmalı" sorusunun da
sorulması gerektiğidir. Bir sayaç asla negatif olmayacaksa işaretsiz bir tip, hem üst
sınırdan bir bit daha geniş bir aralık kazandırır hem de taşmayı tanımlı bir sarmaya
indirger. Bir hesaplama ise ara adımlarda geçici olarak negatife düşebiliyorsa, işaretli
bir tip bu geçişi doğal biçimde taşır, ama üst sınıra yaklaşan bir toplamda tanımsız
davranışa daha yakın durur. Derleyici bu tercihi kaynak yazarının yerine yapmıyor; yalnız
yazılan tip adını okuyup, o adın taşıdığı sınırı ve sınırın aşılma kuralını sabitliyor.

#### Özet

- Bir tipin taşıdığı değer aralığı **derleyicide** karara bağlanır; bu kursta
  `sizeof(char) == 1` dışında hiçbir boyut değeri basılmaz, yalnız karşılaştırma
  sonuçları ölçülür.
- Tam sayı yükseltmesi, `char` ve `short` gibi dar tipleri bir işlemden önce `int`e
  taşır; bu karar işlenen **tiplere** bakılarak verilir, taşınan veriye bakılmaz.
- İşaretsiz tiplerde taşma **tanımlıdır** ve tipin değer sayısına göre sarar; işaretli
  tiplerde taşma **tanımsızdır** ve bu kursta hiçbir kod bloğunda koşturulmaz.
- İşaretli bir değerin işaretsize dönüştürülmesi de tanımlıdır: negatif bir değer, tipin
  değer sayısı eklenerek büyük bir işaretsiz sayıya taşınır.
- Numaralandırma sabitleri (`enum Renk` içindeki `MAVI` gibi) kaynaktaki sabit
  ifadelerden derlemede üretilir; bu konu Yapılar, Birlikler ve Numaralandırmalar
  dersinde derinleşecek.
- Açık bir dönüşüm (`(unsigned char)` gibi), örtük yükseltmenin tersine kaynakta
  görünür ve aynı sarma kuralına tabidir; hangi kuralın uygulanacağı yine hedef tipin
  işaretine bakılarak derlemede belirlenir.

#### Sonraki Adım

Bu derste ölçülen her karar tek bir birimin sınırları içinde kaldı: bir tipin aralığı,
bir işlemin genişliği, bir dönüşümün yönü — hepsi aynı dosyaya bakarak, derleyicinin tek
başına verebileceği kararlardı. Bu derste ölçülen tipler zaten var olan
adlara bağlıydı; bir sonraki soru, o adın kendisinin nereden geldiği ve ne kadar
yaşadığıyla ilgili. Bir adın hangi bildirime bağlandığı ve bir değerin ne
kadar yaşadığı her zaman aynı aşamada karara bağlanmaz. Değişkenler, Kapsam ve Ömür dersi
bu ikisini ayırıyor: bir adın nereden göründüğü derlemede sabitlenirken, bir değerin ne
kadar yaşadığı ancak koşum sırasında gözlenebilir — ve ikisi arasındaki fark, aynı adın
iki ayrı birimde iki ayrı nesneye düşebildiği bir üçüncü aşamayı da gündeme getirecek.

### Değişkenler, Kapsam ve Ömür

Source: https://academia.sh/tr/kurslar/c-temelleri/degiskenler-kapsam-ve-omur

Bir adın nereden göründüğünün derlemede, bir değerin ne kadar yaşadığının koşumda karara bağlanması; aynı adın iki birimde iki ayrı nesneye düşüp düşmediğinin bağlayıcı tarafından çözülmesi.

Veri Tipleri dersi, zaten var olan bir adın taşıdığı tipin nasıl karara bağlandığını
ölçtü. Bu ders bir adım geriye gidiyor: bir adın kendisi nereden görünür ve o adın
işaret ettiği değer ne kadar yaşar? Bu iki soru aynı görünse de aynı aşamada
yanıtlanmıyor — biri kaynağın yapısına, öbürü koşumun akışına ait. Bir üçüncü soru da
bunlara eklenecek: bir program birden çok birimden kurulduğunda, aynı adın iki birimde
aynı nesneye mi yoksa iki ayrı nesneye mi düştüğü de ayrı bir aşamada, ayrı bir kurala
göre çözülüyor.

Programlama Temelleri kursu (M01/K02) kapsam ve ömür kavramlarını Python'la zaten
kurmuştu; orada bir ad bir işlev çağrısı bittiğinde kaybolur, modül düzeyindeki bir ad ise
programın sonuna kadar yaşardı. C'nin karşılığı burada ölçülüyor: kapsam ile ömür C'de
yalnız kavramsal olarak değil, karara bağlandıkları **aşama** bakımından da ayrılıyor —
biri derlemede sabitlenir, öbürü ancak koşum sırasında gözlenebilir.

#### Kapsam: Bir Adın Nereden Göründüğü

Bir adın hangi bildirime bağlandığı, kaynağın yapısına bakılarak derlemede belirlenir.
Aynı ad, iç içe geçmiş bloklarda yeniden bildirildiğinde, en içteki bildirim dıştakini
**gölgeler** — dıştaki ad geçici olarak erişilemez olur, ama yok olmaz.

```c
// kapsam.c — bir adin hangi bildirime baglandigi derlemede belli olur
#include <stdio.h>

int main(void) {
    int deger = 10;
    printf("%s %d\n", "disaridaki deger", deger);
    {
        int deger = 20;
        printf("%s %d\n", "icerideki deger", deger);
    }
    printf("%s %d\n", "blok sonrasi deger", deger);
    return 0;
}
```

```
disaridaki deger 10
icerideki deger 20
blok sonrasi deger 10
```

**TS3.** Blok içindeki `deger` bildirimi, dıştaki `deger`i yalnız kendi bloğunun
sınırları içinde gölgeliyor; blok kapandığında dıştaki ad yeniden görünür oluyor ve
değeri hiç değişmemiş — çünkü iki `deger` aslında iki ayrı nesne, aynı adı taşıyan iki
farklı depolama alanı. Hangi `deger`e erişildiği, programın hangi satırda olduğuna değil,
o satırın kaynakta hangi blok içinde yazıldığına bakılarak belirleniyor; bu bilgi kaynağın
kendisinde tam olarak yazılı ve derleyici onu koşum hiç başlamadan çözüyor. Aynı kural
işlev parametreleri için de geçerlidir: bir işlevin parametre adı, işlev gövdesinin en dış
bloğunda bildirilmiş bir addır ve aynı gölgeleme kuralına tabidir.

Kapsam kuralı yalnız `{ }` ile açılan bloklarla sınırlı değil; bir `for` döngüsünün
başlığında bildirilen sayaç da kendi kapsamını taşır ve döngü bittiğinde o ad artık
görünmez olur.

```c
// dongu_kapsam.c — for basligindaki bildirim, dongunun kendi kapsamina aittir
#include <stdio.h>

int main(void) {
    int i = 100;
    for (int i = 0; i < 3; i++) {
        printf("%s %d\n", "dongu icindeki i", i);
    }
    printf("%s %d\n", "dongu disindaki i", i);
    return 0;
}
```

```
dongu icindeki i 0
dongu icindeki i 1
dongu icindeki i 2
dongu disindaki i 100
```

`for` başlığındaki `i`, dış kapsamdaki `i`yi yalnız döngü gövdesi boyunca gölgeliyor;
döngü bittiğinde dıştaki `i` — hiç dokunulmamış hâliyle, `100` — yeniden görünür oluyor.
Bu, blok gölgelemesinin özel bir biçimi: döngü başlığı kendi başına bir kapsam açıyor ve
bu kapsam yalnız kaynağın yazılış biçiminden, hiçbir veriye bakılmadan belirleniyor.

#### Ömür: Bir Değerin Ne Kadar Yaşadığı

Kapsamın aksine, bir değerin ne kadar yaşadığı kaynağı okuyarak değil, programı çalıştırarak
gözlenir. Aşağıdaki ölçüm aynı işlevi üç kez çağırıyor ve iki farklı bildirim biçiminin
çağrılar arasında nasıl davrandığını sayıyor.

```c
// omur.c — bir degerin ne kadar yasadigi ancak kosarken belli olur
#include <stdio.h>

int otomatik_sayac(void) {
    int sayac = 0;
    sayac++;
    return sayac;
}

int kalici_sayac(void) {
    static int sayac = 0;
    sayac++;
    return sayac;
}

int main(void) {
    for (int i = 0; i < 3; i++) {
        printf("%s %d\n", "cagri", i + 1);
        printf("  %s %d\n", "otomatik sayac", otomatik_sayac());
        printf("  %s %d\n", "kalici sayac", kalici_sayac());
    }
    return 0;
}
```

```
cagri 1
  otomatik sayac 1
  kalici sayac 1
cagri 2
  otomatik sayac 1
  kalici sayac 2
cagri 3
  otomatik sayac 1
  kalici sayac 3
```

`otomatik_sayac` içindeki `sayac`, her çağrıda yeniden doğuyor ve işlev dönerken ölüyor —
bu, C'nin `storage duration` kavramının **ömür** karşılığı ve **M10/K02'nin `yaşam
süresi` (lifetime) terimiyle karıştırılmaz**: orada ölçülen, bir değerin sahiplik
zincirinde ne zaman geçersiz kaldığıydı; burada ölçülen, bir değişkenin bellekte ne kadar
süre ayrılmış kaldığı. `static` belirteci bu süreyi değiştiriyor: `kalici_sayac`
içindeki `sayac`, programın başında bir kez ilklenir ve işlev her döndüğünde ortadan
kalkmaz — bir sonraki çağrı, bir önceki çağrının bıraktığı değeri bulur. Üç çağrı
boyunca `otomatik sayac` hep `1` kalırken `kalici sayac` `1`, `2`, `3` diye ilerliyor;
bu fark kaynakta değil, **kaç kez çağrıldığında** ortaya çıkıyor — kaynağı okuyarak
önceden söylenemez, yalnız bir koşum bunu ortaya koyar.

**TS4.** `static` belirteci burada iki farklı şeyi aynı anda etkilemiyor: `sayac`ın
kapsamı hâlâ işlev gövdesiyle sınırlı — dışarıdan doğrudan erişilemez — ama ömrü artık
işlev çağrısına değil, programın tamamına bağlı. Kapsam ile ömrün ayrı sorular olduğu
tam da burada görünür: aynı bildirimde biri değişirken öbürü sabit kalabiliyor.

Bir dosyanın en dış düzeyinde (herhangi bir işlev gövdesinin dışında) bildirilen bir
değişken zaten programın tamamı kadar yaşar — `birim_sayaci` gibi bir dosya kapsamlı ad,
`static` yazılsın ya da yazılmasın, ömür bakımından en baştan kalıcıdır. `static`
belirtecinin dosya düzeyinde eklediği şey ömür değil, kapsamın **daraltılmasıdır**: aşağıda
ölçülecek olan `kapsam_sayaci` örneğinde görüleceği gibi, kendi biriminin dışına
kapatılır. Yerel bir değişkende ise `static`in etkisi tam tersi yönde çalışıyor — kapsamı
aynı kalırken ömrü uzatıyor. Aynı anahtar sözcük, bildirimin bulunduğu yere göre iki farklı
kararı etkiliyor; bu, `static`in tek bir anlamı olmadığının, bağlamına göre kapsamı ya da
ömrü değiştirebildiğinin bir göstergesi.

#### Kapsam ve Ömrün Bağımsızlığı

Kapsam ile ömür ayrı sorular olduğu için, bir bildirimin ikisinin de farklı bileşimlerini
taşıması mümkün. Yerel bir değişken (blok kapsamı, otomatik ömür) ve yerel bir `static`
değişken (blok kapsamı, kalıcı ömür) yukarıda ölçüldü — ikisi de aynı bloğa hapsolmuş, ama
biri her çağrıda yeniden doğuyor, öbürü kalıcı. Dosya düzeyinde bildirilen bir değişken ise
üçüncü bileşimi gösteriyor: `birim_sayaci` dosya kapsamlıdır — bildirildiği birimin her
yerinden görünür — ve ömrü baştan itibaren kalıcıdır, `static` yazılsın ya da yazılmasın.
Kaynakta olmayan tek bileşim ise dosya kapsamı ile otomatik ömrün birleşimi: dosya
düzeyinde bildirilen bir ad hiçbir zaman bir işlev çağrısına bağlı olarak doğup ölmez,
çünkü dosya kapsamındaki bir bildirim zaten bir çağrı çerçevesinin içinde değildir. Bu
üç bileşim, kapsam ile ömrün gerçekten bağımsız iki eksen olduğunu gösteriyor: biri
"nereden görünür" sorusuna, öbürü "ne kadar yaşar" sorusuna yanıt veriyor ve bir
bildirimin bu iki eksendeki konumu ayrı ayrı seçilebiliyor.

#### Bağlayıcı: Başka Birimdeki Değişken

Kapsam ile ömür bir birimin içinde kalan sorulardı. Bir üçüncü soru, birden çok birim
devreye girdiğinde ortaya çıkıyor: bir ad başka bir birimde tanımlıysa, o ada erişim
ne zaman çözülür? Bu soruyu yanıtlamak için **bildirim** ile **tanım** arasındaki farkı
ayırmak gerekiyor. Bir bildirim, derleyiciye yalnız bir adın tipini ve var olduğunu
söyler — hiçbir bellek ayırmaz. Bir tanım ise o ad için gerçekten bir depolama alanı
oluşturur. `extern int birim_sayaci;` bir bildirimdir; `int birim_sayaci = 2;` ise bir
tanımdır. Bir ad, farklı birimlerde istenildiği kadar çok kez bildirilebilir — her birim
kendi kopyasını tutar — ama harici bağlantılı bir ad için tanım tam olarak **bir kez**
bulunmalıdır; aksi halde bağlayıcı, aynı ada birden fazla depolama alanı ayırma çelişkisiyle
karşılaşır. Aşağıdaki kesit, Derleme Aşamaları dersinde tanıtılan çekirdeğin
bir parçasıdır.

```c
// asama.h — baska birimlerde tanimli adlarin bildirimi (kesit: degisken bildirimi)
#ifndef ASAMA_H
#define ASAMA_H

/* Bu degisken baska bir birimde tanimli: adi burada bildirilir, degeri baglama asamasinda cozulur. */
extern int birim_sayaci;

#endif
```

```c
// secim.c — ikinci birim: degiskenin tanimi burada durur
#include "asama.h"

int birim_sayaci = 2;
```

```c
// asama.c — baska birimdeki degiskenin baglanmasi (kesit)
#include <stdio.h>
#include "asama.h"

int main(void) {
    printf("%s %d\n", "baska birimdeki degisken", birim_sayaci);
    return 0;
}
```

```
baska birimdeki degisken 2
```

`asama.c`, `birim_sayaci` adını yalnız bildirimden (`extern int birim_sayaci;`) tanıyor;
bu bildirim adın tipini ve varlığını garanti ediyor, ama değerini değil. Değer
`secim.c`de tanımlanıyor ve `asama.c`nin derlenmesi sırasında henüz hiçbir yere
bağlanmamış durumda — iki birim ayrı ayrı çevriliyor. Adın gerçek konumunu bulup çağrıyı
buna bağlayan adım **bağlayıcı**dır: iki birimin nesne dosyaları bir araya getirildiğinde,
`asama.c`nin bıraktığı boşluk `secim.c`nin sağladığı tanımla dolduruluyor. Bu, kapsam ve
ömürden ayrı bir üçüncü karardır: kapsam adın **görünürlüğünü**, ömür değerin **süresini**,
bağlama ise adın **hangi tanıma** karşılık geldiğini belirliyor.

Bildirim ile tanım arasındaki bu ayrım, başlık dosyalarının neden yalnız bildirim
taşıdığını da açıklıyor: `asama.h` birden fazla birime dahil edilebilir (her ikisi de
`#include "asama.h"` yazabilir) ve her dahil edilişte yalnız bir bildirim eklenir, bir
tanım değil. Eğer başlık dosyası `int birim_sayaci = 2;` gibi bir tanım taşısaydı, onu
dahil eden her birim kendi tanımını üretir ve bağlayıcı aynı ad için birden fazla
depolama alanıyla karşılaşırdı. Bildirim ile tanımın ayrılması, bu yüzden yalnız bir
sözdizimi ayrıntısı değil, çok birimli bir programın derlenebilmesinin ön koşulu.

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

Bir adın iki birimde aynı yazılması, o adın tek bir nesneye karşılık geldiği anlamına
gelmez. `birim_sayaci` gibi bir `extern` değişken bilerek **paylaşılmak** için
tasarlanmıştı; ama `static` bir değişken tam tersini garanti eder — kendi birimine
kapanır ve başka hiçbir birim ona doğrudan erişemez. Bu, aynı adın iki ayrı birimde iki
ayrı nesneye düşebileceği anlamına geliyor ve bunu belirleyen bağlayıcıdır.

```c
// birim_a.c — bu birimde tanimli, disaridan erisilemeyen bir kapsam sayaci
static int kapsam_sayaci = 11;

int a_dan_oku(void) {
    return kapsam_sayaci;
}
```

```c
// birim_b.c — ayni adi tasiyan, ama ayri bir nesne
static int kapsam_sayaci = 22;

int b_den_oku(void) {
    return kapsam_sayaci;
}
```

```c
// sinirlayici.c — ayni ad iki birimde iki ayri nesne, karari baglayici verir
#include <stdio.h>

int a_dan_oku(void);
int b_den_oku(void);

int main(void) {
    printf("%s %d\n", "a biriminden okunan", a_dan_oku());
    printf("%s %d\n", "b biriminden okunan", b_den_oku());
    return 0;
}
```

```
a biriminden okunan 11
b biriminden okunan 22
```

`birim_a.c` ve `birim_b.c`, ikisi de `kapsam_sayaci` adında bir değişken tanımlıyor, ama
`static` belirteci her ikisini de kendi birimine kapatıyor. Bağlayıcı bu iki tanımı asla
karşılaştırmıyor ve bir çakışma raporlamıyor — çünkü ikisi de kendi birimlerinin dışına
hiç çıkmıyor, sembol tablosunda görünmüyor. `a_dan_oku` yalnız kendi biriminin
`kapsam_sayaci`sını, `b_den_oku` yalnız kendisininkini görebiliyor ve sonuçlar bunu
doğruluyor: `11` ve `22`, birbirinden tamamen bağımsız. Bu, `extern` bir değişkenin
davranışının tam tersi: orada tek bir tanım vardı ve bağlayıcı bütün çağrıları o tek
tanıma bağlıyordu; burada iki ayrı tanım var ve bağlayıcı ikisini hiç karşılaştırmadan,
her birini kendi biriminde bırakıyor. Aynı ad, `static` belirtecine bağlı olarak, bir
bağlayıcı kararının iki tamamen farklı sonucuna yol açabiliyor.

Bu üçüncü aşamanın pratik sonucu, `static`in dosya düzeyinde bir gizleme aracı olarak
kullanılabilmesidir. Bir birim, kendi iç işleyişi için bir sayaç ya da yardımcı bir
değişken tutmak isteyip bunu başka hiçbir birimin görmesini istemiyorsa, o değişkeni
`static` bildirir; ad geri kalan birimlerle çakışsa bile — `kapsam_sayaci` örneğinde
olduğu gibi — bağlayıcı bir çakışma raporlamaz, çünkü her ikisi de kendi biriminin
sınırları içinde kalır. `extern` bir değişken ise tam tersi bir sözleşme kurar: birimler
arasında kasıtlı olarak paylaşılan, tek bir tanıma sahip bir addır. Kaynak yazarı hangi
ismin paylaşılacağını, hangisinin kendi biriminde kalacağını `static` anahtar sözcüğünün
varlığıyla veya yokluğuyla belirler — ve bu tercih, birimler bağlanırken bağlayıcının
izleyeceği kuralı önceden belirlemiş olur.

#### Özet

- Bir adın **kapsamı** — nereden göründüğü — kaynağın blok yapısına bakılarak
  **derlemede** belirlenir; iç içe bloklarda aynı ad dıştakini gölgeler.
- Bir değerin **ömrü** — ne kadar yaşadığı — ancak **koşumda** gözlenebilir; otomatik
  bir yerel değişken her çağrıda yeniden doğar, `static` bir yerel değişken çağrılar
  arasında kalıcıdır.
- `ömür` burada C'nin `storage duration` karşılığıdır; M10/K02'nin `yaşam süresi`
  (`lifetime`) teriminden ayrı tutulur — biri depolama süresini, öbürü sahiplik
  geçerliliğini ölçer.
- `extern` bir değişken tek bir tanıma bağlanır ve birimler arasında paylaşılır; bu
  bağlanma **bağlayıcı** aşamasında, birimler bir araya geldiğinde çözülür.
- Aynı ad iki birimde `static` olarak tanımlandığında, bağlayıcı bunları hiç
  karşılaştırmadan iki ayrı nesne olarak bırakır — kapsam, ömür ve bağlama üç ayrı
  soru olduğu için üçü de aynı anda, ama birbirinden bağımsız karara bağlanabilir.
- Kapsam ile ömür bağımsız eksenlerdir: blok kapsamlı bir ad otomatik ya da kalıcı
  ömürlü olabilir, dosya kapsamlı bir ad ise her zaman kalıcı ömürlüdür — dosya
  kapsamı ile otomatik ömrün birleşimi kaynakta hiç ortaya çıkmaz.

#### Sonraki Adım

Bu derste ölçülen kararların hiçbiri, bir ifadenin sonucunu neyin belirlediğine
değinmedi — yalnız bir adın nereden göründüğü, bir değerin ne kadar yaşadığı ve bir
tanımın hangi birimde durduğu ölçüldü. İşleçler dersi bu zemine yeni bir soru ekliyor:
aynı işleç, işlendiği değerlerin tipine göre farklı bir sonuç üretebiliyor ve bu seçim
de veriye değil, derlemede bilinen tiplere bakılarak yapılıyor. `7 / 2` ile `7 / 2.0`
aynı işleçle yazılsa da, hangi bölmenin çalışacağı işlenenlerin tipinden geliyor —
tıpkı bu derste bir adın hangi bildirime bağlandığının kaynağın yapısından gelmesi gibi.

### İşleçler

Source: https://academia.sh/tr/kurslar/c-temelleri/islecler

Bir işlecin sonucunun işlenenlerin tipine göre derlemede karara bağlanması; öncelik, kısa devre değerlendirmesi, bileşik atama ve değerlendirme sırasının tanımlı-tanımsız ayrımı üzerinden ölçülmesi.

Değişkenler, Kapsam ve Ömür dersi, bir adın nereden göründüğünü ve bir değerin ne kadar
yaşadığını ölçtü — ikisi de bir *adın* durumuyla ilgiliydi. Bu ders bir adım öteye
geçiyor: iki değer bir işleçle birleştirildiğinde, sonucu neyin belirlediğini ölçüyor.
Yanıt, işlenenlerin değeri değil, tipidir — ve bu seçim, tıpkı önceki derste bir adın
hangi bildirime bağlandığı gibi, derlemede karara bağlanıyor. Bu ders dört ayrı işleç
ailesini — aritmetik, mantıksal, bitsel ve atama — aynı soruyla ölçüyor: kaynakta yazılan
bir işlecin sonucu, ya da o sonuca varmak için hangi alt işlemlerin çalışacağı, koşum
başlamadan önce ne kadarı zaten bellidir?

Programlama Temelleri kursu (M01/K02) aritmetik ve mantıksal işleçleri Python'la zaten
kurmuştu; orada bir işlecin davranışı çoğunlukla işlenenlerin çalışma zamanındaki
değerine bakılarak yorumlanırdı. C'nin karşılığı burada ölçülüyor: bir işlecin **hangi**
işlemi yürüteceği, kaynakta yazılan işlenenlerin **tipine** bakılarak, işlem hiç
çalışmadan önce derlemede sabitleniyor.

#### İşlecin Tipe Göre Seçimi

Aynı `/` işleci, işlenenlerinin tipine göre iki farklı işlem yürütür: ikisi de tam sayıysa
tam sayı bölmesi, en az biri kayan noktalıysa kayan nokta bölmesi. Bu seçim veriye değil,
kaynakta yazılan tiplere bakılarak yapılıyor.

```c
// asama.c — islecin tipe gore secimi (kesit)
#include <stdio.h>

int main(void) {
    int tam = 7 / 2;
    double kesirli = 7 / 2.0;
    printf("tam %d kesirli %.1f\n", tam, kesirli);
    return 0;
}
```

```
tam 3 kesirli 3.5
```

**TS5.** Kaynakta yazılan tek fark, `2` ile `2.0` — biri tam sayı sabiti, öbürü kayan
nokta sabiti. Bu fark, hangi bölme işleminin çalışacağını derlemede belirliyor: `7 / 2`
tam sayı bölmesi yürütüyor ve kesirli kısmı atıyor, `7 / 2.0` ise `7`yi önce `double`a
yükseltip kayan nokta bölmesi yürütüyor. `7`nin kendisi hiç değişmedi; değişen, aynı `/`
işlecinin hangi donanım işlemine karşılık geldiği — kaynakta yazılan iki karakterlik
işleç sembolü aynı kalırken, derleyicinin ürettiği makine talimatı işlenenlerin tipine
göre iki farklı yol izliyor. Bu, önceki derste ölçülen tam sayı
yükseltmesiyle aynı ailede bir karar: işleç, işlenenlerin bildirilen tipine bakıyor,
çalışma zamanındaki değerine değil.

Bu kural yalnız bölme işlecine özgü değil; C standardının **olağan aritmetik
dönüşümler** dediği kural, hemen hemen bütün ikili aritmetik işleçler için aynı şekilde
işler. İki işlenenin tipi farklıysa, "dar" olan taraf "geniş" olana yükseltilir ve işlem
geniş tipte yürütülür — tıpkı Veri Tipleri dersinde `char` işlenenlerin `int`e
yükseltilmesi gibi, burada da bir `int` ile bir `double` toplandığında `int` önce
`double`a dönüştürülür. Kaynakta `tam_sayac + oran` gibi bir ifade yazıldığında, hangi
işlenenin hangi yöne dönüştürüleceği iki tarafın bildirilen tipine bakılarak
derlemede belirlenir; hangi değerlerin toplanacağı bu kararı hiç etkilemez.

#### Öncelik: Kaynaktaki Yapının Derlemede Sabitlenmesi

Bir işlecin ne zaman çalışacağı yalnız tipe değil, kaynaktaki yapıya da bağlıdır.
Öncelik kuralları, parantezsiz bir ifadenin hangi alt ifadelerden önce
değerlendirileceğini belirler ve bu karar tamamen kaynağın kendisinde yazılıdır.

```c
// oncelik.c — islec onceligi kaynaktaki yapiyi derlemede sabitler
#include <stdio.h>

int main(void) {
    int carpma_once = 2 + 3 * 4;
    int toplama_once = (2 + 3) * 4;
    printf("%s %d\n", "carpma once (parantezsiz)", carpma_once);
    printf("%s %d\n", "toplama once (parantezli)", toplama_once);
    return 0;
}
```

```
carpma once (parantezsiz) 14
toplama once (parantezli) 20
```

İki ifade de aynı üç sayıyı (`2`, `3`, `4`) ve aynı iki işleci (`+`, `*`) taşıyor, ama
sonuçları farklı: `carpma_once` çarpmayı toplamadan önce yürütüyor (`2 + 12 = 14`),
`toplama_once` ise parantez sayesinde toplamayı önce yürütüyor (`5 * 4 = 20`). Bu fark
hiçbir veriye bakmadan, yalnız kaynaktaki parantezlerin varlığına bakılarak derlemede
çözülüyor — koşum başlamadan önce hangi alt ifadenin hangi sırada değerlendirileceği
zaten belli.

Öncelik yalnız hangi işlecin önce çalışacağını değil, aynı önceliğe sahip işleçler yan
yana yazıldığında hangi yönden gruplanacağını da belirler — buna **birleşme yönü**
denir. Çoğu ikili işleç (`+`, `-`, `*`, `/`) soldan sağa birleşir: `10 - 3 - 2` ifadesi
`(10 - 3) - 2` gibi gruplanır, `10 - (3 - 2)` gibi değil ve bu iki gruplama farklı
sonuçlar verir (`5` ile `9`). Atama işleci ise tam tersi yönde, sağdan sola birleşir:
`a = b = 5` ifadesi `a = (b = 5)` gibi gruplanır, önce `b`ye `5` atanır, sonra o atamanın
sonucu `a`ya atanır. Hem öncelik hem birleşme yönü, koşum hiç başlamadan, yalnız kaynaktaki
işleç dizilişine bakılarak derlemede çözülür.

#### Kısa Devre: Hangi İşlecin Seçildiği İkinci İşleneni Etkiler

Mantıksal işleçler (`&&`, `||`) bir garanti taşır: sol işlenen sonucu belirlemeye
yetiyorsa, sağ işlenen hiç değerlendirilmez. Bitsel işleçler (`&`, `|`) aynı garantiyi
taşımaz — iki işlenen de her zaman değerlendirilir. Hangi garantinin geçerli olduğu,
kaynakta hangi işlecin yazıldığına bakılarak derlemede belirlenir.

```c
// kisadevre.c — hangi islecin secildigi ikinci islenenin calisip calismayacagini derlemede belirler
#include <stdio.h>

static int cagri_sayaci = 0;

static int yan_etkili(void) {
    cagri_sayaci++;
    return 1;
}

int main(void) {
    cagri_sayaci = 0;
    int ve_sonucu = (0 && yan_etkili());
    printf("%s %d\n", "ve isleci sonrasi cagri sayaci", cagri_sayaci);

    cagri_sayaci = 0;
    int bitsel_sonucu = (0 & yan_etkili());
    printf("%s %d\n", "bitsel islec sonrasi cagri sayaci", cagri_sayaci);

    (void)ve_sonucu;
    (void)bitsel_sonucu;
    return 0;
}
```

```
ve isleci sonrasi cagri sayaci 0
bitsel islec sonrasi cagri sayaci 1
```

**TS6.** İki çağrı da aynı `yan_etkili` işlevini, aynı `0` sol işleneniyle kullanıyor,
ama sonuç farklı: `&&` ile sol işlenen zaten yanlış olduğu için sağ işlenen hiç
çalıştırılmıyor ve `cagri_sayaci` sıfırda kalıyor; `&` ile ise sol işlenenin değeri
sonucu belirlemeye yetse de, işlecin kendisi böyle bir kısayolu tanımıyor ve
`yan_etkili` yine de çağrılıyor. Hangi garantinin uygulanacağı, koşum sırasında
`cagri_sayaci`nin gerçekten `0` mı `1` mi olacağını belirleyen bir karar — ama bu kararın
**kendisi**, hangi işlecin seçildiğine bakılarak, derleme sırasında sabitleniyor. İki
işlecin ikisi de aynı sol işlenenle aynı mantıksal sonucu (`0`) üretirdi; aralarındaki
fark sonuçta değil, ikinci işlenenin çalışıp çalışmayacağında.

Bu ayrım, `yan_etkili` gibi bir çağrının yalnızca **gerektiğinde** çalışmasını sağlamak
istendiğinde önem kazanıyor. Sol işlenen tek başına sonucu belirlemeye yetiyorsa (`&&`
için yanlış, `||` için doğru), kısa devre garantisi sağ tarafın hiç çalışmamasını
sağlıyor — bu, gereksiz bir işi atlamanın kaynakta yazılan tek satırla elde edilen bir
yoludur. Bitsel işleçler bu garantiyi taşımadığı için, iki işlenen de her koşulda
çalışır; bu yüzden bitsel işleçler mantıksal bir koşul için değil, gerçekten bit
düzeyinde bir birleştirme gerektiğinde tercih edilir.

#### Bileşik Atama: Sol Tarafın Tipine Göre Dönüşüm

Bir bileşik atama işleci (`+=`, `|=` gibi) sonucu her zaman sol tarafın **bildirilen**
tipine dönüştürür — sağ tarafın tipi ne olursa olsun.

```c
// atama.c — bilesik atama sonucu sol tarafin tipine gore derlemede donusturulur
#include <stdio.h>

int main(void) {
    int tam_sayi = 5;
    tam_sayi += 2.7;
    printf("%s %d\n", "int uzerinde bilesik atama", tam_sayi);

    unsigned char bayrak = 0xF0;
    bayrak |= 0x0F;
    printf("%s %d\n", "unsigned char uzerinde bilesik atama", bayrak);

    int carpan = 6;
    carpan *= 2.5;
    printf("%s %d\n", "int uzerinde carpimsal atama", carpan);

    int sayac = 10;
    sayac -= 3;
    printf("%s %d\n", "int uzerinde cikarma atamasi", sayac);
    return 0;
}
```

```
int uzerinde bilesik atama 7
unsigned char uzerinde bilesik atama 255
int uzerinde carpimsal atama 15
int uzerinde cikarma atamasi 7
```

`tam_sayi += 2.7`, açılımıyla `tam_sayi = tam_sayi + 2.7` gibi çalışır: toplam önce
`double` genişliğinde hesaplanır (`5 + 2.7 = 7.7`), ama sonuç `tam_sayi`nın tipi `int`
olduğu için kesirli kısım atılarak `7`ye dönüştürülür. Bu dönüşüm sağ taraftaki `2.7`nin
değerine bakılarak değil, sol taraftaki `tam_sayi`nın bildirilen tipine bakılarak
uygulanıyor — sağ taraf `2.1` ya da `2.9` olsaydı da aynı kesme kuralı işlerdi.
`bayrak |= 0x0F` ise bitsel bir birleştirme: `0xF0` ile `0x0F`in bit düzeyinde birleşimi
`0xFF`, ondalıkla `255`. `carpan *= 2.5` aynı kuralı bir kez daha doğruluyor: `6 * 2.5 =
15.0`, `double` genişliğinde tam sayı değerli bir sonuç ve `carpan`ın tipi `int`
olduğu için hiç kesme kaybı olmadan `15`e dönüşüyor — kesmenin her zaman bir kayıp
anlamına gelmediğini, yalnız sonucun sol tarafın tipine **zorlandığını** gösteriyor.
`sayac -= 3` ise iki tarafın da zaten aynı tipte olduğu, dönüşümün görünmediği sıradan
durum. Dört örnekte de sonucun tipi sol tarafın bildirilen tipiyle sabitleniyor; bu,
atama işlecinin de tıpkı bölme işleci gibi tip odaklı çalıştığını gösteriyor.

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

İşleç önceliği, kaynaktaki alt ifadelerin **hangi sırayla gruplanacağını** belirler, ama
bir ifadenin bütün alt bölümlerinin **hangi sırayla değerlendirileceğini** her zaman
belirlemez. `f(a, b)` gibi bir çağrıda `a` ile `b`nin hangisinin önce hesaplanacağı
belirtilmemiştir; eğer `a` ve `b` aynı değişkeni yan etkiyle değiştiriyorsa — örneğin
ikisi de aynı sayacı artırıyorsa — bu, standardın **tanımsız davranış** olarak
sınıflandırdığı bir durumdur: aynı nesne, aralarında bir sıralama noktası olmadan iki kez
değiştiriliyor. Bu kursun genel kuralınca böyle bir çağrı kod bloğunda **denenmiyor**.

Yanına konan tanımlı karşılık, virgül işlecidir: `,` ile ayrılan iki alt ifade her zaman
soldan sağa, aralarında tam bir sıralama noktasıyla değerlendirilir — sol taraf
tamamlanmadan sağ taraf başlamaz.

```c
// sinir.c — virgul isleci sirayi garanti eder, fonksiyon argumanlari garanti etmez
#include <stdio.h>

int main(void) {
    int i = 0;
    int son_deger = (i++, i++);
    printf("%s %d\n", "virgul isleciyle son deger", son_deger);
    printf("%s %d\n", "virgul isleciyle i", i);
    return 0;
}
```

```
virgul isleciyle son deger 1
virgul isleciyle i 2
```

`(i++, i++)` ifadesinde sol taraftaki `i++` önce tamamen çalışır: `i`nin eski değerini
(`0`) üretir ve `i`yi `1`e taşır — ama bu değer virgül işleci tarafından atılır. Ardından
sağ taraftaki `i++` çalışır: bu kez `i`nin (artık `1` olan) değerini üretir ve `i`yi
`2`ye taşır; ifadenin toplam sonucu sağ tarafın ürettiği değer, yani `1`. Sonuç
tamamen öngörülebilir ve her koşumda aynı: virgül işleci, fonksiyon çağrısının argüman
listesinin **vermediği** garantiyi tam olarak veriyor. Aynı `i++, i++` deseni bir
fonksiyon çağrısının iki bağımsız değişkeni olarak yazılsaydı (`f(i++, i++)` gibi),
derleyicinin argümanları hangi sırayla değerlendireceği belirtilmemiş olurdu ve aynı
`i`nin iki kez değiştirilmesi tanımsız davranışa düşerdi — bu ders o çağrıyı
denemiyor, yalnız virgül işlecinin garantili sırasını ölçüyor.

Aynı sınıftaki bir başka klasik örnek `i + i++` gibi bir ifadedir: toplama işlecinin iki
işleneni arasında da bir sıralama garantisi yoktur ve sol taraftaki `i` okunurken sağ
taraftaki `i++`nin bu okumadan önce mi sonra mı `i`yi değiştirdiği belirsizdir — aynı
nesne okunuyor ve değiştiriliyor, aralarında bir sıra garantisi olmadan. Bu da bu kursun
genel kuralınca koşturulmuyor. Buradaki ortak nokta, C'nin çoğu işlecinin işlenenleri
arasında **hiçbir sıralama garantisi vermediğidir** — sıralama yalnız birkaç özel yapı
için (virgül işleci, `&&`, `||`, `?:`in koşul kısmı) açıkça garanti edilir. Bir ifadede
aynı değişkeni birden fazla kez, en az biri değiştirerek kullanmak isteyen bir kaynak
yazarı, bu garantili yapılardan birine ya da ayrı ifadelere yaslanmak zorundadır;
aksi hâlde derleyicinin hangi sırayı seçeceği belirsiz kalır.

Bu derste ölçülen kararların tümü aynı soruya bağlanıyor: bir işlecin sonucu, işlenen
tiplerine ve kaynaktaki yapıya bakılarak **derlemede** mi belirleniyor, yoksa
işlenenlerin gerçek değerine bakılarak **koşumda** mı? Bölme, öncelik, kısa devre ve
bileşik atama için yanıt derleyiciydi — hangi işlemin çalışacağı ya da hangi sıranın
izleneceği, program hiç çalışmadan önce kaynaktan okunabiliyordu. Değerlendirme
sırasının tanımsız kaldığı durumlar ise bu kuralın istisnası değil, sınırı: derleyicinin
garanti **etmediği** yerde, kaynak yazarının kendi garantisini kurması gerekiyor.

#### Özet

- Bir işlecin **hangi işlemi** yürüteceği, işlenenlerin çalışma zamanındaki değerine
  değil, kaynakta yazılan **tiplerine** bakılarak **derlemede** karara bağlanır — sınıf
  **derleyici**.
- İşleç önceliği, parantezsiz bir ifadenin hangi alt ifadelerin önce gruplanacağını
  kaynağın kendi yapısından, hiçbir veriye bakmadan belirler; birleşme yönü aynı
  önceliğe sahip işleçler yan yana yazıldığında hangi taraftan gruplanacağını verir.
- `&&` ve `||` kısa devre garantisi taşır, `&` ve `|` taşımaz; hangi garantinin
  geçerli olduğu, kaynakta hangi işlecin yazıldığına bakılarak derlemede sabitlenir.
- Bileşik atama işleçleri, sonucu her zaman sol tarafın bildirilen tipine dönüştürür —
  bu dönüşüm sağ tarafın değerine değil, sol tarafın tipine bakılarak yapılır.
- Fonksiyon çağrısı argümanlarının değerlendirme sırası belirtilmemiştir ve aynı
  değişkeni sıralama noktası olmadan iki kez değiştirmek tanımsız davranıştır; bu
  kursta koşturulmaz. Virgül işleci ise soldan sağa garantili bir sıra taşır ve
  tanımlı karşılık olarak koşturuldu.

#### Sonraki Adım

Bu derste ölçülen kararların hepsi tek bir ifadenin sınırları içinde kaldı: bir işlecin
hangi işlemi yürüttüğü, hangi alt ifadenin önce gruplandığı, hangi işlenenin
değerlendirildiği. Bu kararların ortak özelliği, sonucu üreten **verinin** hiçbir zaman
kararı etkilememesiydi — yalnız kaynakta yazılan tipler ve yapı belirleyiciydi. Denetim
Akışı dersi bu zemine yeni bir soru ekliyor: bir programın hangi **dalı** izleyeceği
artık kaynakta değil, koşumda üretilen bir veriye bakılarak belirleniyor. Bu derste
`carpma_once` gibi bir sonucun hangi işlemden geldiği kaynaktan okunabiliyordu; sıradaki
derste bir dalın hangi kod yolunu izleyeceği, ancak program çalışıp veriyi ürettikten
sonra bilinebilecek — kaynağı okuyarak önceden söylenemeyecek.

### Denetim Akışı

Source: https://academia.sh/tr/kurslar/c-temelleri/denetim-akisi

Bir dalın hangi kod yolunu izleyeceğinin koşumda üretilen veriye bakılarak belirlenmesi; switch düşmesi, döngü biçimleri, goto'nun sınırlı yeri ve hangi dalların var olduğu ile hangisinin seçildiği arasındaki ayrım.

İşleçler dersi, bir ifadenin sonucunu neyin belirlediğini ölçtü ve yanıt hep aynıydı:
kaynakta yazılan tipler ve yapı, koşum başlamadan önce zaten biliniyordu. Denetim akışı
bu düzeni tersine çeviriyor. Bir program hangi dalı izleyecek, hangi döngü kaç kez
dönecek — bunlar artık kaynağı okuyarak önceden söylenemez; ancak program çalışıp veriyi
ürettikten sonra bilinir.

Programlama Temelleri kursu (M01/K02) `if`, `while` ve benzeri denetim yapılarını
Python'la zaten kurmuştu; oradaki sözdizimi ile buradaki arasında yüzeysel bir benzerlik
var, ama bu ders sözdizimini değil, **kararın hangi aşamada verildiğini** ölçüyor: bir
dalın seçilip seçilmediği veriye, hangi dalların kaynakta **var olduğu** ise ayrı bir
aşamaya bağlı olabiliyor.

Bu ders M11'in ölçü ekseni bakımından bir dönüm noktası. Veri Tipleri, Değişkenler ve
İşleçler dersleri hep aynı yöne işaret etti: kaynakta yazılan tipler ve yapı, koşum hiç
başlamadan kararı veriyordu. Denetim akışında bu yön tersine döner — bir dalın seçilip
seçilmeyeceği artık kaynağı okuyarak değil, programı çalıştırıp veriyi üreterek bilinir.
Ama bu tersine dönüş mutlak değil: aşağıda görüleceği gibi, hangi dalların kaynakta bile
**mevcut olduğu** hâlâ derleyiciden önceki bir aşamada, ön işlemcide karara bağlanabiliyor.
Denetim akışı, bu yüzden tek bir sınıfa değil, iki sınıfın kesişimine oturan bir konu.

#### Veriye Bağlı Dallanma

Aşağıdaki kesit, Derleme Aşamaları dersinde tanıtılan çekirdeğin bir parçasıdır: bir
dizinin toplamı hesaplanıyor ve bu toplama bakılarak bir dal seçiliyor.

```c
// asama.c — veriye bagli dallanma (kesit)
#include <stdio.h>
#include <stddef.h>

int main(void) {
    int veri[] = {4, 8, 15, 16};
    int toplam = 0;
    for (size_t i = 0; i < sizeof veri / sizeof veri[0]; i++) {
        toplam += veri[i];
    }
    const char *karar = (toplam > 40) ? "buyuk" : "kucuk";
    printf("%s %s\n", "veriye bagli dallanma", karar);
    return 0;
}
```

```
veriye bagli dallanma buyuk
```

**TS7.** `karar` değişkeninin `"buyuk"` mu `"kucuk"` mu olacağı, kaynakta hiçbir yerde
yazılı değil — `veri` dizisindeki dört sayı toplanana kadar bilinemez. `toplam`
`43` çıktığı için `"buyuk"` dalı seçiliyor, ama diziye tek bir eleman eklense ya da bir
eleman değişse sonuç değişebilirdi; kaynak kodun kendisi hiç değişmeden. Bu, önceki
derste ölçülen işleç seçimiyle taban tabana zıt bir karar: orada sonucu tipler
belirliyordu ve koşum başlamadan biliniyordu, burada sonucu veri belirliyor ve ancak
döngü tamamlandıktan sonra bilinebiliyor.

Bu fark `?:` koşullu işlecinin kendisinde de görünür. `(toplam > 40) ? "buyuk" :
"kucuk"` ifadesinin **hangi dalının** değer üreteceği koşuma bağlı, ama ifadenin
**kendisinin bir değer ürettiği** derlemede zaten bellidir — derleyici iki daldan ortak
bir sonuç tipi türetir (burada ikisi de aynı olduğu için `const char *`) ve ikisinden
birinin seçileceğini bilir, yalnız hangisinin seçileceğini bilmez. Bu, denetim akışının
genel bir özelliğini gösteriyor: kaynak, hangi dalların **var olduğunu** ve her dalın
hangi tipte bir sonuç üreteceğini derlemede sabitler; yalnız veriye bakılarak
hangisinin **çalışacağı** koşuma bırakılır. `if` gövdeleri ve `switch` case'leri için de
aynı ayrım geçerli: kaynaktaki her dal derlenir ve ikili dosyada yerini alır, ama bir
koşumda hangisinin gerçekten yürütüldüğü ancak o koşum bittikten sonra söylenebilir.

#### Switch Düşmesi

Bir `switch` ifadesinde art arda gelen `case` etiketleri, aralarında `break` olmadıkça
aynı gövdeye **düşer** (fall-through) — bir etiketten bir sonrakine akış kesilmeden
geçilir. **`düşme` burada `fall-through` karşılığıdır ve M06/K05'te kapalıdır** — bu
kullanım o dersteki `drop` duyusundan tamamen ayrı, C'de `drop` kavramı zaten yok.

```c
// gecisli.c — switch dusmesi (fall-through) art arda etiketleri ayni govdeye baglar
#include <stdio.h>

static void seviyeyi_yazdir(int puan) {
    switch (puan / 10) {
        case 10:
        case 9:
            printf("%s\n", "seviye: A");
            break;
        case 8:
        case 7:
            /* kasitli dusme: 8 ve 7, ortak govdeye dusuyor */
            printf("%s\n", "seviye: B");
            break;
        default:
            printf("%s\n", "seviye: C");
            break;
    }
}

int main(void) {
    seviyeyi_yazdir(95);
    seviyeyi_yazdir(88);
    seviyeyi_yazdir(72);
    seviyeyi_yazdir(40);
    return 0;
}
```

```
seviye: A
seviye: B
seviye: B
seviye: C
```

`puan / 10` ifadesi `10` verdiğinde akış gövdesiz `case 10` etiketine giriyor, hiçbir
gövde bulmadan `case 9`a düşüyor ve oradaki gövdeyi çalıştırıyor; `9` verdiğinde ise
doğrudan `case 9`a atlıyor. İki etiket böylece tek bir gövdeyi paylaşıyor. Aynı düzen
`case 8` ile `case 7` için de geçerli. `break`
yazılmadığında akışın devam etmesi C'nin varsayılan davranışı; bir gövdeden çıkmak
isteyen her `case` bunu **açıkça** `break` ile belirtmek zorunda. Yukarıdaki dört çağrı
bunu doğruluyor: `95` gövdeli `case 9`a eşleşiyor ve düşmeye hiç uğramıyor; `88` ise
gövdesiz `case 8`e eşleşip `case 7`nin gövdesine düşüyor.

Düşmenin kendisi, hangi `case`in eşleştiğine bakılmaksızın kaynakta **her zaman
mevcuttur** — derleyici, `break` yazılmayan her etiketten bir sonrakine akan bir yol
üretir ve bu yol ikili dosyada durur. Ama bu yolun **izlenip izlenmeyeceği**, yalnız
`switch` ifadesinin başındaki değere bakılarak koşumda belirlenir: `95` girildiğinde akış
zaten `case 10`dan başlamıyor, doğrudan `case 9`a atlıyor, çünkü `switch` bir eşleşen
etiketi bulduğu anda oraya sıçrar ve yalnız **oradan sonrasını** sırayla yürütür. Düşme,
bu yüzden "her etiket sırayla denenir" anlamına gelmiyor — `switch` önce eşleşen etiketi
buluyor, düşme yalnız o noktadan **sonraki** etiketleri, aralarında `break` yoksa,
gövdesiz geçiyor.

#### Döngü Biçimleri: Koşulun Ne Zaman Sınandığı

C üç temel döngü biçimi taşır ve aralarındaki fark, koşulun döngü gövdesinden **önce**
mi **sonra** mı sınandığıdır — bu da veriye bağlı bir davranış farkına yol açar.

```c
// donguler.c — while ve do-while ayni kosulu farkli calisma sayisiyla yorumlar
#include <stdio.h>

int main(void) {
    int sayac = 5;
    int calisma_sayisi_while = 0;
    while (sayac < 5) {
        calisma_sayisi_while++;
        sayac++;
    }
    printf("%s %d\n", "while calisma sayisi", calisma_sayisi_while);

    int calisma_sayisi_dowhile = 0;
    do {
        calisma_sayisi_dowhile++;
    } while (sayac < 5);
    printf("%s %d\n", "do-while calisma sayisi", calisma_sayisi_dowhile);
    return 0;
}
```

```
while calisma sayisi 0
do-while calisma sayisi 1
```

`sayac` baştan `5` ve `while (sayac < 5)` koşulu gövdeden **önce** sınandığı için, koşul
en baştan yanlış ve gövde hiç çalışmıyor: `calisma_sayisi_while` sıfırda kalıyor.
`do { ... } while (sayac < 5)` ise gövdeyi **önce çalıştırıp sonra** koşulu sınıyor;
koşul yine yanlış olsa da gövde en az bir kez çalışmış oluyor ve `calisma_sayisi_dowhile`
`1` çıkıyor. İki döngü de aynı koşulu taşıyor, aynı veriyle çalışıyor, ama sınama sırası
farklı olduğu için sonuçları ayrışıyor — bu fark, koşulun kaynakta nerede yazıldığına
değil, döngünün hangi biçimde bildirildiğine bağlı ve `for` döngüsü de `while` gibi
koşulunu gövdeden önce sınar.

Üç biçimin de gövdesi kaynakta yalnız bir kez yazılır, ama kaç kez **çalışacağı**
koşumdaki veriye bağlıdır — `sayac`ın başlangıç değeri farklı olsaydı, aynı üç döngü
farklı sayıda dönerdi. `while` ile `for` arasındaki fark yalnız yazım biçimidir: `for
(baslangic; kosul; adim)` bir `while` döngüsünün üç parçasını tek satırda toplar ve
ikisi de aynı sınama sırasını (koşul önce) izler. `do-while` ise tek biçimde farklı: bir
döngünün gövdesi koşula bakılmaksızın en az bir kez çalışmalıysa — bir menü göstermek,
bir girdi istemek gibi — bu biçim, ayrı bir bayrak değişkeni ya da gövdeyi iki kez yazmak
zorunda kalmadan bunu doğrudan ifade eder.

#### Goto: Sınırlı ve Kabul Edilen Bir Kullanım

`goto`, kaynağın herhangi bir yerinden bir etikete doğrudan atlar ve genel kullanımı
akışı izlemeyi zorlaştırdığı için önerilmez. Ama ic ice geçmiş döngülerden **tek adımda**
çıkmak gibi dar bir senaryoda, `break`in yalnız en yakın döngüyü kırabilmesi yüzünden
`goto` yaygın kabul gören bir çözüm sunar.

```c
// arama.c — goto, ic ice donguden tek adimda cikmanin sinirli ve kabul edilen kullanimi
#include <stdio.h>

int main(void) {
    int izgara[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int hedef = 6;
    int bulunan_i = -1, bulunan_j = -1;

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (izgara[i][j] == hedef) {
                bulunan_i = i;
                bulunan_j = j;
                goto bulundu;
            }
        }
    }
bulundu:
    printf("%s %d\n", "bulunan satir", bulunan_i);
    printf("%s %d\n", "bulunan sutun", bulunan_j);
    return 0;
}
```

```
bulunan satir 1
bulunan sutun 2
```

`hedef` değeri `izgara[1][2]`de bulunuyor ve `goto bulundu;` iki döngüyü de tek satırda
terk ediyor. Aynı sonucu `break` ile elde etmek isteyen bir yazım, iç döngüden çıktıktan
sonra dış döngüyü de bir bayrak değişkenle kontrol edip ayrıca kırmak zorunda kalırdı —
`goto` burada ek bir bayrak değişkeni gerektirmeden aynı işi görüyor. `bulundu`
etiketinin döngülerin dışında, işlevin sonuna yakın durması da kabul gören kullanımın bir
parçası: `goto` yalnız ileri doğru ve işlevin doğal akışının bittiği bir noktaya atlıyor,
kaynağın ortasında karmaşık bir sıçrama ağı kurmuyor.

`bulundu` etiketine gerçekten atlanıp atlanmayacağı da veriye bağlı bir karar: `hedef`
değeri `izgara` içinde hiç bulunmasaydı, iki döngü de tamamen bitmiş ve `goto` hiç
çalışmamış olurdu, akış etikete yalnız döngülerin doğal sonunda, sırayla ulaşırdı.
Kaynakta `goto bulundu;` satırı hep aynı yerde duruyor, ama o satırın çalışıp
çalışmayacağı — ve çalışırsa kaçıncı döngü adımında — `izgara`nın içeriğine ve `hedef`in
değerine bakılarak koşumda belirleniyor. `goto` bu yüzden denetim akışının genel
kuralından ayrılmıyor: sıçramanın kendisi kaynakta sabit, sıçramanın **tetiklenip
tetiklenmeyeceği** veriye bağlı.

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

Bir dalın hangi kod yolunu izleyeceği veriye bağlıydı, ama hangi dalların kaynakta **var
olduğu** her zaman koşumla belirlenmez — bazen ön işlemci, derleyici kaynağı görmeden
önce, hangi `case`lerin bile var olacağına karar vermiş olabilir.

```c
// sinir.c — hangi dallarin var oldugu on islemcide, hangisinin secildigi kosumda belirlenir
#include <stdio.h>

#define EK_DURUM

static void kodu_degerlendir(int kod) {
    switch (kod) {
        case 1:
            printf("%s\n", "durum 1 calisti");
            break;
#ifdef EK_DURUM
        case 2:
            printf("%s\n", "durum 2 calisti (on islemci varligini onayladi)");
            break;
#endif
        default:
            printf("%s\n", "taninmayan durum");
            break;
    }
}

int main(void) {
    for (int kod = 1; kod <= 3; kod++) {
        kodu_degerlendir(kod);
    }
    return 0;
}
```

```
durum 1 calisti
durum 2 calisti (on islemci varligini onayladi)
taninmayan durum
```

`EK_DURUM` makrosu tanımlı olduğu için `case 2` etiketi, derleyici kaynağı görmeden
**önce**, ön işlemci tarafından metne dahil ediliyor. `EK_DURUM` tanımlı olmasaydı, o
`case` bloğu kaynaktan tamamen silinmiş olurdu — derleyici onu hiç görmez, `kod == 2`
geldiğinde akış doğrudan `default`a düşerdi. Bu ölçümde `EK_DURUM` tanımlı olduğu için
`case 2` var ve `kod` döngüde `1`den `3`e kadar ilerlerken hangi durumun **seçildiği**
her adımda veriye (döngü sayacına) bakılarak koşumda belirleniyor. İki karar birbirinden
tamamen bağımsız: bir dalın **var olup olmadığı** ön işlemcide, o dalın **seçilip
seçilmediği** koşumda karara bağlanıyor — ve bir dal hiç var olmazsa, hiçbir veri onu
seçemez.

Bu iki aşamanın sırası tersine çevrilemez. Ön işlemci önce çalışır ve `#ifdef` bloklarını
kaynaktan ya tamamen siler ya da tamamen bırakır; derleyici, kendisine ulaşan metinde
zaten hangi `case`lerin bulunduğunu bilir, hiçbir zaman "bu `case` şu veriyle var olsun"
diye bir karar vermez. Koşum ise tam tersi bir sınırlamayla çalışır: elindeki ikili
dosyada zaten hangi dalların bulunduğunu değiştiremez, yalnız var olanlar arasından
seçim yapabilir. Bir hatanın kaynağını ararken bu ayrım pratik bir sonuç doğurur: "bu
dal hiç çalışmadı" ile "bu dal zaten kaynakta yok" iki ayrı olası neden ve ikisinin
denetlendiği yer de farklı — biri koşum çıktısında, öbürü kaynağın ön işlemci
yönergelerinde aranır.

#### Özet

- Bir dalın hangi kod yolunu izleyeceği **koşumda** üretilen veriye bakılarak belirlenir;
  kaynak kodun kendisi bu kararı önceden vermez — sınıf **koşum**.
- `switch` içinde `break` olmayan `case` etiketleri bir sonraki gövdeye **düşer**;
  bu düşme, `break` kaynakta açıkça yazılmadıkça C'nin varsayılan davranışıdır.
- `while` koşulu gövdeden önce, `do-while` koşulu gövdeden sonra sınanır; aynı koşul ve
  aynı veriyle bile bu sıra farkı çalışma sayısını değiştirebilir.
- `goto`, iç içe döngülerden tek adımda çıkmak gibi dar ve kabul gören bir senaryoda,
  ek bir bayrak değişkeni gerektirmeden aynı işi görür.
- Hangi dalların kaynakta **var olduğu** ön işlemcide, var olan dallardan **hangisinin
  seçildiği** koşumda karara bağlanabilir — iki karar aynı `switch` içinde bir arada
  bulunabilir ve birbirinden bağımsızdır.
- `?:` koşullu işleci bir değer ürettiğini ve o değerin tipini derlemede sabitler; tip iki
  daldan türetilir, yalnız hangi dalın gerçekten değer üreteceği koşumda belirlenir.

#### Sonraki Adım

Bu derste ölçülen dallanmalar hep bilinen bir işlevin gövdesi içinde kaldı — hangi dalın
seçileceği veriye bağlıydı, ama hangi işlevin çağrıldığı hep kaynakta sabitti. Fonksiyonlar
dersi bu son varsayımı sınıyor: bir çağrının hangi tanıma bağlanacağı, iki birim ayrı ayrı
derlendikten sonra, ancak bağlama sırasında çözülüyor. Yukarıdaki `kodu_degerlendir` gibi
bir işlev hep aynı dosyada tanımlıydı; sıradaki derste bir işlev bir birimde çağrılacak,
tanımı ise başka bir birimde duracak — ve aradaki bağı kim, hangi aşamada kurduğunu
belirleyecek. `toplam > 40` gibi bir sınamanın sonucu bu derste yalnız `veri` dizisine
bağlıydı; sıradaki derste bir çağrının sonucu, ayrıca çağrılan işlevin **hangi tanıma**
ulaştığına da bağlı olacak — kaynakta yazılan bir ad, iki farklı birimde iki farklı
gövdeye karşılık gelemez, ama hangi gövdeye bağlandığını yalnız bağlayıcı bilir.

### Fonksiyonlar

Source: https://academia.sh/tr/kurslar/c-temelleri/fonksiyonlar

Bir çağrının başka bir birimdeki tanıma bağlanmasının bağlayıcı aşamasında çözülmesi; bildirim-tanım ayrımı, parametre aktarımının her zaman kopya olması ve dönüş değerinin tipinin çağıran tarafın yorumunu belirlemesi.

Denetim Akışı dersi, hep aynı işlevin içinde kalan bir soruyla ilgilendi: hangi dal
seçilecek. Bu ders o sınırı kaldırıyor ve bir çağrının işlev **sınırını** nasıl geçtiğini
ölçüyor. Bir işlev çağrıldığında üç ayrı soru birden çözülüyor: çağrının hangi tanıma
bağlanacağı, argümanların işleve nasıl ulaştığı ve dönen değerin nasıl yorumlanacağı —
üçü de aynı anda, ama farklı gerekçelerle karara bağlanan sorular. İlki bağlayıcıya
aittir, çünkü birden çok birim gerektirir; öbür ikisi ise derleyicinin tek bir birime
bakarak verdiği kararlardır — bir çağrının tam olarak nereye bağlanacağını bilmese bile,
derleyici argümanların nasıl kopyalanacağını ve dönüşün nasıl yorumlanacağını bildirimden
çıkarabilir.

Programlama Temelleri kursu (M01/K02) fonksiyon kavramını Python'la zaten kurmuştu;
orada bir işlevin çağrılabilmesi için önce tanımlanmış olması yeterliydi ve
bildirim-tanım ayrımı yoktu. C'nin karşılığı burada ölçülüyor: bir çağrı, tanımı hiç
görmeden, yalnız bir **bildirime** dayanarak derlenebiliyor — tanımın kendisi başka bir
birimde durabiliyor ve iki tarafı birbirine bağlayan adım ayrı bir aşamaya ait. Bu
derste ölçülen üç karar — bağlanma, aktarım, dönüş — bir işlevin çağrılma yüzeyinin üç
ayrı bileşeni ve her biri kendi aşamasında, birbirinden bağımsız olarak sabitleniyor.

#### Bağlayıcı: Başka Birimdeki İşlev

Aşağıdaki kesit, Derleme Aşamaları dersinde tanıtılan çekirdeğin bir parçasıdır: bir
işlev bir birimde çağrılıyor, tanımı ise başka bir birimde duruyor.

```c
// asama.h — baska birimlerde tanimli adlarin bildirimi (kesit: islev bildirimi)
#ifndef ASAMA_H
#define ASAMA_H

/* Bu islev baska bir birimde tanimli: cagrisi baglama asamasinda cozulur. */
int kimlik(void);

#endif
```

```c
// secim.c — ikinci birim: islevin tanimi burada durur
#include "asama.h"

int kimlik(void) {
    return 7;
}
```

```c
// asama.c — baska birimdeki islevin baglanmasi (kesit)
#include <stdio.h>
#include "asama.h"

int main(void) {
    printf("%s %d\n", "baska birimdeki islev", kimlik());
    return 0;
}
```

```
baska birimdeki islev 7
```

**TS8.** `asama.c` derlenirken `kimlik`in gövdesi hiçbir yerde görünmüyor — derleyici
yalnız `asama.h`deki bildirimi görüyor ve bu bildirimden, çağrının doğru sayıda ve doğru
tipte argüman aldığını, dönüş tipinin `int` olduğunu denetleyip nesne dosyasına bir
"bu ada ihtiyacım var" notu bırakıyor. `secim.c` ayrı ayrı derleniyor ve kendi nesne
dosyasına `kimlik`in gerçek gövdesini yazıyor. İki nesne dosyası bir araya getirildiğinde
bağlayıcı, `asama.c`nin bıraktığı notu `secim.c`nin sağladığı gövdeyle eşleştiriyor —
bu eşleşme olmadan çalıştırılabilir bir program hiç oluşmaz. Kapsam ve ömür bir birimin
içinde kalan sorulardı; bir çağrının hangi gövdeye bağlanacağı ise ancak birimler bir
araya geldiğinde, bağlayıcı aşamasında çözülüyor.

Bu üç aşamalı akış — önce iki birim ayrı ayrı derlenir, sonra bağlayıcı ikisini
birleştirir — Derleme Aşamaları dersinde ölçülen genel düzenin işlevlere uygulanmış
hâli. `asama.c`nin derlenmesi `secim.c`nin var olup olmadığından habersiz ilerler;
derleyici yalnız `asama.h`deki bildirime bakarak "bu isimde, bu imzada bir işlev bir
yerlerde tanımlı olmalı" varsayımıyla çalışır ve bu varsayımı doğrulamak bağlayıcının
işidir. Eğer `secim.c` hiç derlenmemiş ya da `kimlik` tanımı yanlışlıkla silinmiş olsaydı,
`asama.c`nin derlenmesi yine sorunsuz tamamlanırdı — hata ancak bağlama adımında,
bağlayıcı `kimlik` adını hiçbir nesne dosyasında bulamadığında ortaya çıkardı. Bu, bir
derleme hatasıyla bir bağlama hatasının neden farklı aşamalarda, farklı zamanlarda
ortaya çıkabildiğini gösteriyor.

Bu düzenek, Değişkenler, Kapsam ve Ömür dersinde ölçülen `extern` değişken bağlanmasıyla
aynı ailededir: orada bir adın hangi **depolama alanına**, burada bir adın hangi
**gövdeye** karşılık geldiği bağlayıcıda çözülüyor. Fark, işlevlerin varsayılan olarak
dış bağlantılı olması — bir değişkeni paylaşmak için `extern` yazmak gerekirken, bir
işlev `static` yazılmadıkça zaten birimler arasında görünür ve çağrılabilir durumdadır.
`kimlik` işlevi `static` olmadığı için `secim.c`deki tanımı `asama.c`den görülebiliyor;
işlev `static kimlik(void)` diye tanımlansaydı, kendi birimine kapanır ve `asama.c`nin
derlenmesi, bağlayıcının aynı adı bulamaması yüzünden başarısız olurdu.

Bu, `static` anahtar sözcüğünün işlevler için de değişkenlerdekiyle aynı işlevi
gördüğünü doğruluyor: kapsamı dosya sınırına daraltıyor ve bağlayıcının o adı başka
birimlere göstermesini engelliyor. Bir işlevi `static` bildirmek, o işlevin yalnız kendi
biriminin iç işleyişine ait bir yardımcı olduğunu, dışarıya bir arayüz olarak
sunulmadığını kaynakta açıkça işaretler; bu, bir birimin kendi iç ayrıntılarını
gizleyebilmesinin işlevler düzeyindeki karşılığı.

#### Bildirim ile Tanımın Ayrılması

Bir bildirim, derleyiciye bir işlevin imzasını (adı, parametre tipleri, dönüş tipi)
söyler; bir tanım ise gövdeyi de içerir. `asama.h`deki `int kimlik(void);` bir
bildirimdir — noktalı virgülle biter, gövde taşımaz. `secim.c`deki `int kimlik(void) {
return 7; }` ise bir tanımdır. Bir işlev, farklı birimlerde (aynı başlık dosyası
aracılığıyla) istenildiği kadar çok kez bildirilebilir, ama tanımı tam olarak **bir kez**
bulunmalıdır — aksi hâlde bağlayıcı aynı ada birden fazla gövde bulur ve bu bir çelişkidir.
Bu ayrım, önceki derste değişkenler için ölçülen bildirim-tanım ayrımının işlevlere
uygulanmış hâli: `extern int birim_sayaci;` bir değişken bildirimiyken `int
birim_sayaci = 2;` tanımdı; burada da aynı örüntü, gövde yerine yalnız imza taşıyan bir
bildirimle tekrar ediyor.

Bir işlev bildirimi olmadan, yalnız çağrılma biçimine bakarak bir çağrının doğru olup
olmadığını denetlemek mümkün değildir; derleyici, kaç parametre beklendiğini ve hangi
tipte olduklarını yalnız bildirimden öğrenir. `asama.h`nin `asama.c` tarafından dahil
edilmesi tam olarak bunu sağlıyor: `asama.c` derlenirken `kimlik`in gövdesini hiç
görmüyor, ama parametresiz ve `int` döndüren bir işlev olduğunu biliyor ve çağrıyı buna
göre derliyor. Bildirim, bu yüzden yalnız bir belge değil — derleyicinin çağrıyı doğru
biçimde üretebilmesi için ihtiyaç duyduğu asgari bilgi.

#### Parametre Aktarımı: Her Zaman Kopya

C'de bir işleve geçirilen her argüman, işlevin kendi parametresine **kopyalanır**.
İşlev parametresinde yapılan bir değişiklik, çağıran taraftaki değişkeni hiçbir zaman
etkilemez.

```c
// kopya.c — parametre aktarimi her zaman kopyadir, cagiranin degiskenini etkilemez
#include <stdio.h>

static void ikiye_katla(int deger) {
    deger = deger * 2;
    printf("%s %d\n", "islev icinde deger", deger);
}

int main(void) {
    int sayi = 5;
    ikiye_katla(sayi);
    printf("%s %d\n", "cagiran taraftaki sayi", sayi);
    return 0;
}
```

```
islev icinde deger 10
cagiran taraftaki sayi 5
```

Ölçüm iki satır yazdırıyor: işlev içindeki `deger`, çarpma sonrası `10`; çağıran
taraftaki `sayi` ise hâlâ `5`. `ikiye_katla` çağrıldığında, `sayi`nın değeri (`5`)
`deger` adlı yeni bir depolama alanına kopyalanıyor; `deger`in içindeki değişiklik yalnız o kopyayı etkiliyor,
`main`deki `sayi`ya hiç dokunmuyor. İşlev döndüğünde `deger` ortadan kalkıyor —
Değişkenler, Kapsam ve Ömür dersinde ölçülen otomatik ömrün bir örneği — ve `sayi` hiç
değişmemiş hâliyle kalıyor. Bu davranış, işlevin aldığı parametrenin türü ne olursa
olsun (tam sayı, kayan nokta, yapı) aynıdır: C'de aktarım her zaman **değer** aktarımıdır,
kaynaktaki değişkenin kendisi hiçbir zaman doğrudan işleve geçmez.

Bu kopyalama, kaç parametre olursa olsun aynı şekilde işler ve işlevin gövdesinin
uzunluğundan bağımsızdır: `deger` adı hem çağıranın `sayi`sinden hem de işlevin kendi
gövdesinden **ayrı** bir depolama alanına karşılık gelir, işlev çağrıldığı anda
oluşturulur ve döndüğünde ortadan kalkar. Bu, Değişkenler, Kapsam ve Ömür dersinde
ölçülen otomatik ömür kuralının parametrelere de uygulandığını gösteriyor: bir
parametre, tıpkı gövde içinde bildirilen sıradan bir yerel değişken gibi davranır —
yalnız ilk değeri çağıran taraftan gelir. Çağıran tarafın değişkenini gerçekten
değiştirmek isteyen bir kaynak yazarı, C'de bunu değeri değil, o değerin **adresini**
geçirerek yapar; ama adresin kendisi bu derste basılmıyor ve bu konu, bir kursun
tamamına ayrılmış işaretçiler konusunun parçası.

#### Dönüş Değerinin Tipi

Bir işlevin dönüş tipi, gövde içinde hangi ifadenin hesaplandığından bağımsız olarak,
çağıranın o değeri nasıl yorumlayacağını belirler.

```c
// donus.c — donus degerinin tipi, cagiran tarafin degeri nasil yorumladigini belirler
#include <stdio.h>

static int oranla(int pay, int payda) {
    double gercek_oran = (double)pay / (double)payda;
    return (int)(gercek_oran * 100);
}

int main(void) {
    int yuzde = oranla(1, 3);
    printf("%s %d\n", "int donus tipiyle yuzde", yuzde);
    return 0;
}
```

```
int donus tipiyle yuzde 33
```

`oranla` içinde `gercek_oran` bir `double` ve `1 / 3` tam sayı bölmesi yerine kayan
nokta bölmesiyle hesaplanıyor (`0.333...`). Ama işlevin dönüş tipi `int` olarak
bildirildiği için, `return` ifadesindeki değer — `33.333...` — çağırana ulaşmadan önce
`int`e dönüştürülüyor ve kesirli kısım atılıyor: `33`. Bu dönüşüm, işlev gövdesinin
neresinde hangi tipin kullanıldığına bakılmaksızın, yalnız işlevin **bildirilen** dönüş
tipine bakılarak uygulanıyor — tıpkı bileşik atamada sonucun sol tarafın tipine
zorlanması gibi, burada da sonuç işlevin imzasında yazılı tipe zorlanıyor. `oranla(1,
3)` ve `oranla(1, 4)` gibi farklı çağrılar farklı gerçek oranlar üretse de, ikisi de aynı
`return` ifadesinden, aynı `int`e daraltma kuralından geçer — hangi girdiyle
çağrıldığına bakılmaksızın, dönüşüm kuralının kendisi değişmez.

Bu dönüşümün yeri önemlidir: `gercek_oran * 100` ifadesi hâlâ `oranla`nın gövdesi
içinde, `double` genişliğinde hesaplanıyor; `int`e daraltma yalnız `return` ifadesinin
değeri işlevin dışına çıkarken, tam olarak işlev sınırında uygulanıyor. Çağıran taraf
(`main`), `oranla`nın içinde hangi ara tiplerin kullanıldığını hiç bilmez ve bilmesi de
gerekmez — yalnız bildirimdeki dönüş tipini (`int`) görür ve gelen değeri o tipte bir
değişkene (`yuzde`) atar. Bu, işlev sınırının bir tür **kapsülleme** sağladığını
gösteriyor: gövde içindeki hesaplama hangi ara tipleri kullanırsa kullansın, dışarıya
sızan tek bilgi, imzada yazılı dönüş tipidir.

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

Bir çağrı, çağrılan işlevin bildirimiyle **uyumlu** olduğu sürece güvenle derlenir ve
bağlanır — yukarıdaki `dogru.c` örneği bunu gösteriyor.

```c
// asama.h — cagrinin bicimini derleyiciye bildiren tam bildirim
#ifndef ASAMA_H
#define ASAMA_H

int kimlik(void);

#endif
```

```c
// secim.c — tanim, bildirimle ayni imzayi tasir
#include "asama.h"

int kimlik(void) {
    return 7;
}
```

```c
// dogru.c — bildirimle tanim ayni imzada oldugunda cagri guvenle baglanir
#include <stdio.h>
#include "asama.h"

int main(void) {
    printf("%s %d\n", "dogru bildirimli cagri", kimlik());
    return 0;
}
```

```
dogru bildirimli cagri 7
```

Bu ortamda, bildirimi hiç görünmeyen bir işlevi çağırmayı deneyen bir kaynak derleyici
tarafından doğrudan reddediliyor — derleyici, bir çağrının biçimini denetleyebilmek için
en azından bir bildirime ihtiyaç duyuyor ve onsuz kaynağı derlemeyi durduruyor. Ama bu,
bütün uyuşmazlıkları derleyicinin yakaladığı anlamına gelmiyor. Bir bildirim, yalnız o
bildirimin bulunduğu **birimde** denetlenir; derleyici `asama.c`yi derlerken `secim.c`nin
içine hiç bakmaz, `secim.c`deki gerçek tanımın bildirimle **gerçekten** eşleştiğini
doğrulayan bir mekanizma yoktur. C'nin bağlayıcısı da sembol adına bakar, tipe bakmaz —
iki birim aynı isimde ama farklı parametre tipleriyle bir işlevi bildirip tanımlasa,
bağlayıcı bunları yine de birbirine bağlar. Böyle bir çağrı, derleneni ve bağlananı
çalıştırdığında tanımsız davranışa düşer, çünkü çağıran taraf argümanları bir biçimde
hazırlar, çağrılan taraf onları başka bir biçimde okur; bu kursun genel kuralınca böyle
bir uyumsuzluk kod bloğunda **denenmiyor**. Ders yalnız yukarıdaki doğru bildirimli
kurguyu koşturuyor: bildirim çağrının biçimini derleyiciye söyler, ama tanımla
gerçekten eşleştiğini garanti eden şey, kaynak yazarının kendi tutarlılığıdır — ne
derleyici ne bağlayıcı bunu birimler arasında denetler.

Bu sınırın pratik sonucu, bir başlık dosyasının neden **tek bir doğruluk kaynağı**
olarak tutulduğudur. `kimlik`i çağıran her birim `asama.h`yi dahil ettiği ve `secim.c`
de aynı başlığı dahil ettiği sürece, bildirim ile tanım aynı kaynaktan geldiği için
birbirleriyle otomatik olarak tutarlı kalır — kaynak yazarı iki farklı yerde aynı
imzayı elle iki kez yazmak zorunda kalmaz. Bir birim başlığı dahil etmeyip işlevin
imzasını kendi kaynağında elden yazsaydı, iki bildirim kaynakta ayrı ayrı durur ve
biri değiştirildiğinde öbürünün fark edilmeden eskimesi mümkün olurdu — tam da
yukarıda anlatılan riskin kaynağı. Başlık dosyasını paylaşmak, bu riski ortadan
kaldırmaz ama en azından imzanın **bir kez** yazılmasını, dolayısıyla en az bir
kaynaktan gelen tutarlılığı garanti eder.

#### Özet

- Bir çağrının **hangi tanıma** bağlanacağı, birimler bir araya geldiğinde **bağlayıcı**
  tarafından çözülür; derleyici yalnız bildirimden imzayı denetler, gövdeyi görmez.
- Bir bildirim yalnız imzayı (ad, parametre tipleri, dönüş tipi) taşır; bir tanım
  gövdeyi de içerir. Bir işlev çok kez bildirilebilir, ama tam olarak bir kez tanımlanır.
- Parametre aktarımı her zaman **değer** aktarımıdır: işlev içinde yapılan bir
  değişiklik, çağıranın kendi değişkenini hiçbir zaman etkilemez.
- Bir işlevin dönüş değeri, gövde içindeki ara hesaplamaların tipinden bağımsız olarak,
  işlevin **bildirilen** dönüş tipine dönüştürülür.
- Bir bildirim ile o adın gerçek tanımı arasındaki uyum, ne derleyici ne bağlayıcı
  tarafından birimler arasında denetlenir; bu tutarlılığı korumak kaynak yazarının işi.
- `static` bir işlev kendi birimine kapanır ve bağlayıcı tarafından başka hiçbir birime
  gösterilmez; işlevler için de değişkenlerdeki gizleme kuralı aynen geçerlidir.

#### Sonraki Adım

Bu derste ölçülen kararların hepsi, bir işlevin çağrılma **biçimiyle** ilgiliydi:
hangi tanıma bağlandığı, argümanın nasıl aktarıldığı, dönüş değerinin nasıl
yorumlandığı. Diziler ve Dizgiler dersi bu zemine, bir verinin **uzunluğunun** ne zaman
belli olduğu sorusunu ekliyor: bir dizinin eleman sayısı kaynakta sabit bir ifadeden
derlemede üretilebilirken, bir dizginin uzunluğu ancak koşumda, sonlandırıcı bayta
ulaşılana kadar taranarak öğrenilebiliyor — ve bu derste ölçülen "parametre her zaman
kopyadır" kuralı, bir dizi işleve geçtiğinde ilginç bir istisnaya dönüşecek: bir dizi
adı işleve geçirildiğinde, kopyalanan şey dizinin kendisi değil, dizinin ilk elemanının
adresi olacak — ve bu, uzunluk bilgisinin neden ayrıca taşınması gerektiğinin de
kaynağı.

### Diziler ve Dizgiler

Source: https://academia.sh/tr/kurslar/c-temelleri/diziler-ve-dizgiler

Bir dizinin eleman sayısının sabit ifade katlamasıyla derlemede, bir dizginin uzunluğunun sonlandırıcı bayta ulaşılana kadar koşumda belirlenmesi; dizi-işaretçi ilişkisinin adres basılmadan eleman değeri ve farkla ölçülmesi.

Fonksiyonlar dersi, bir çağrının hangi tanıma bağlandığını, argümanın nasıl kopyalandığını
ve dönüşün nasıl yorumlandığını ölçtü. Bu ders, aktarılan verinin kendisine dönüyor ve iki
farklı veri türünün uzunluğunu nasıl taşıdığını karşılaştırıyor: bir **dizi**nin uzunluğu
ile bir **dizgi**nin uzunluğu, aynı bellek düzenine dayansa da, iki ayrı aşamada karara
bağlanıyor.

Programlama Temelleri kursu (M01/K02) liste ve dizgi kavramlarını Python'la zaten kurmuştu;
orada bir listenin uzunluğu her zaman bir çağrıyla (`len`) sorulabilen, kendiliğinden
taşınan bir bilgiydi. C'nin karşılığı burada ölçülüyor: bir dizinin uzunluğu kaynakta
yazılan bir sabit ifadeden **derlemede** üretilirken, bir dizginin uzunluğu hiçbir yerde
saklanmaz — yalnız sonlandırıcı bir bayta ulaşılana kadar **koşumda** taranarak öğrenilir.
Bu ders, kursun ölçü ekseninin iki ucunu aynı anda gösteren tek ders: aynı bellek
düzeninde duran iki komşu değer, biri için "kaynak yeter", öbürü için "yalnız koşum
yeter" yanıtını veriyor.

#### Sabit İfade Katlaması: Dizi Uzunluğu Derlemede

Aşağıdaki kesit, Derleme Aşamaları dersinde tanıtılan çekirdeğin bir parçasıdır: bir
dizinin boyutu, kaynakta yazılan bir sabit ifadeden üretiliyor.

```c
// asama.c — sabit ifade katlamasi (derleyici) ve dizgi uzunlugu (kosum) kesiti
#include <stdio.h>
#include <string.h>

int main(void) {
    int sabit_dizi[2 * 3 + 1];
    printf("%s %d\n", "sabit ifade katlamasi", (int)(sizeof sabit_dizi / sizeof sabit_dizi[0]));

    const char *metin = "merhaba dunya";
    printf("%s %d\n", "dizgi uzunlugu", (int)strlen(metin));
    return 0;
}
```

```
sabit ifade katlamasi 7
dizgi uzunlugu 13
```

**TS9.** `2 * 3 + 1` ifadesi hiçbir değişkene bağlı değil — yalnız sabitlerden kurulu ve
derleyici bu ifadeyi program hiç çalışmadan **katlayarak** `7`ye indirgiyor;
`sabit_dizi`nin eleman sayısı, ikili dosyaya zaten `7` olarak yazılıyor. `strlen(metin)`
ise tam tersi: `metin` işaretçisinin gösterdiği bayt dizisi, `\0` bulunana kadar bir bir
taranıyor ve uzunluk (`13`) ancak bu tarama bitince ortaya çıkıyor. İki ölçüm de aynı
satırda, aynı işlevde duruyor, ama biri kaynaktan okunabilecek bir sayıyı doğruluyor,
öbürü ancak çalıştırılarak öğrenilebilecek bir sayıyı üretiyor.

`sabit_dizi`nin boyutu ikili dosyaya gömüldüğü için, `sizeof sabit_dizi` ifadesinin
kendisi de koşum sırasında hiçbir hesaplama gerektirmez — derleyici bu ifadeyi zaten bir
sabitle değiştirmiştir. Bu, `sizeof` işlecinin çoğu kullanımında **derleme zamanı**
bir işleç olduğunu gösteriyor: işlenenin gerçek değerine hiç bakmadan, yalnız tipine
bakarak bir sonuç üretiyor. `sabit_dizi[2 * 3 + 1]` gibi bir bildirimde parantez içindeki
ifade de aynı kategoriye giriyor — dizinin boyutunu belirleyen ifade, bir **sabit
ifade** olmak zorunda; içine bir değişken ya da bir işlev çağrısı girseydi, derleyici
boyutu artık önceden bilemez ve bu, C'nin ayrı bir konusu olan değişken uzunluklu
dizilere girerdi. Bu derste ölçülen dizi hep sabit uzunlukludur.

#### Sonlandırıcı Baytın Rolü: Dizgi Uzunluğu Koşumda

Bir dizginin uzunluğu hiçbir yerde ayrıca saklanmaz; `strlen` ve benzeri işlevler, bir
`\0` baytına rastlayana kadar belleği tarayarak uzunluğu **hesaplar**. Sonlandırıcı
baytın konumu değiştiğinde, aynı bellek alanı farklı bir uzunluk raporlar.

```c
// sonlandirici.c — sonlandirici baytin konumu, dizginin nerede bittigini kosumda belirler
#include <stdio.h>
#include <string.h>

int main(void) {
    char kisa[6] = {'m', 'e', 'r', 'h', 'a', '\0'};
    printf("%s %d\n", "sonlandirici sonda iken uzunluk", (int)strlen(kisa));

    kisa[2] = '\0';
    printf("%s %d\n", "sonlandirici erken konunca uzunluk", (int)strlen(kisa));
    return 0;
}
```

```
sonlandirici sonda iken uzunluk 5
sonlandirici erken konunca uzunluk 2
```

**TS10.** `kisa` dizisi altı bayt ayırıyor ve ilk beşi harf, altıncısı `\0`; `strlen`
`5` döndürüyor çünkü sonlandırıcıya beşinci bayttan sonra rastlıyor. `kisa[2]`
`'\0'`a değiştirildiğinde, dizinin kendisi hâlâ altı bayt — hiçbir bellek yeniden ayrılmadı,
hiçbir eleman silinmedi — ama `strlen` artık `2` döndürüyor, çünkü tarama üçüncü bayta
ulaşır ulaşmaz duruyor. Bu, bir dizginin "uzunluğu"nun aslında bir depolama özelliği
değil, bir **koşum sonucu** olduğunu gösteriyor: aynı bellek düzeni, sonlandırıcının
konumuna bağlı olarak farklı bir uzunluk raporlayabiliyor ve bu konum çalışma zamanında
değişebilen sıradan bir bayt.

Bu, C'de bir dizginin neden **kendi başına** bir tip olmadığını da açıklıyor: `char
kisa[6]` bir `char` dizisidir, dilin gözünde altı ayrı `char` değerinden başka bir şey
taşımaz. "Dizgi", yalnız bir **kural** — içindeki bir yerde bir `\0` bayt bulunacağı ve
bu baytın sınırı işaretleyeceği kuralı — üzerine kurulu bir yorumdur, dilin kendisinin
denetlediği bir özellik değil. Bir tam sayı dizisi (`int dizi[]`) için böyle bir kural
yoktur — hangi elemanın "sonu" işaretlediğine dair bir sözleşme dilin bir parçası
değildir; bu yüzden bir tam sayı dizisinin uzunluğu ya kaynakta sabit bir ifadeyle
(yukarıdaki `sabit_dizi` gibi) belirlenir ya da ayrıca bir değişkende taşınır. Dizgi,
yalnız `char` dizileri için, sonlandırıcı bayt sözleşmesiyle, kendi uzunluğunu kendi
içinde taşıyan bir özel durumdur. `kisa[2]`ye elle `'\0'` yazmak, dizinin kendi bellek
düzenini hiç bozmuyor; yalnız bu kuralın hangi baytta durduğunu değiştiriyor. `strlen`
gibi işlevler bu kuralı **varsayar** — kendilerine geçirilen bir işaretçinin gösterdiği
bellekte bir yerde gerçekten bir `\0` bulunacağını umarlar; sonlandırıcı hiç yoksa,
tarama dizinin ayrılmış sınırlarının ötesine taşar ve bu, kursun genel kuralınca
denenmeyen bir tanımsız davranış senaryosudur.

#### Dizi-İşaretçi İlişkisi: Adres Basmadan Ölçüm

Bir dizi adı, çoğu bağlamda ilk elemanının işaretçisine **dönüşür** (decay). Bu ilişki
adres basılmadan, yalnız eleman değeri ve fark üzerinden ölçülebilir.

```c
// iliski.c — dizi ile isaretci iliskisi, adres basmadan deger ve fark uzerinden olculur
#include <stdio.h>

int main(void) {
    int dizi[] = {10, 20, 30, 40};
    int *p = dizi;

    printf("%s %d\n", "p artimliyla erisilen deger", *(p + 2));
    printf("%s %d\n", "dizi indeksiyle erisilen deger", dizi[2]);
    printf("%s %d\n", "iki erisim ayni mi", (int)(*(p + 2) == dizi[2]));
    printf("%s %d\n", "adresler ayni mi", (int)((p + 2) == &dizi[2]));
    printf("%s %d\n", "eleman cinsinden fark", (int)(&dizi[3] - &dizi[0]));
    return 0;
}
```

```
p artimliyla erisilen deger 30
dizi indeksiyle erisilen deger 30
iki erisim ayni mi 1
adresler ayni mi 1
eleman cinsinden fark 3
```

**TS11.** `p = dizi` atamasıyla `p`, dizinin ilk elemanının adresini alıyor; `*(p + 2)`
ile `dizi[2]` aynı değeri (`30`) üretiyor, çünkü `dizi[i]` zaten `*(dizi + i)`nin kısa
yazımı — ikisi de aynı adresi hesaplayıp aynı değeri okuyor. `(p + 2) == &dizi[2]`
karşılaştırması da bunu doğruluyor: iki adres eşit. `&dizi[3] - &dizi[0]` farkı ise ham
bir bayt farkı değil, **eleman cinsinden** bir fark — sonuç `3`, çünkü iki işaretçi
arasında üç `int` genişliğinde adım var; bu fark işlecinin C'de işaretçiler için özel
olarak tanımlanmış bir davranışı, `int` genişliğine bölünmüş hâliyle veriliyor. Hiçbir
adım gerçek bir adres yazdırmadı; ölçülen her şey bir değer, bir eşitlik ya da bir
eleman farkıydı.

`dizi[2]` yazımının `*(dizi + 2)`nin yalnız kısa bir yazımı olması, C'nin dizi
indekslemeye ayrı bir mekanizma ayırmadığını gösteriyor: köşeli parantez, işaretçi
aritmetiğinin üstüne kurulu bir sözdizimi kolaylığı. Bu, `dizi[2]` ile `2[dizi]`
yazımlarının aynı anlama gelmesinin de nedeni — toplama işleci değişmeli olduğu için
`dizi + 2` ile `2 + dizi` aynı adresi verir ve iki yazım da aynı çeviriden geçer. Ders
bu eşdeğerliği ölçmüyor, ama `dizi[i]`nin altında yatan işlemin gerçekten bir toplama ve
bir yönlendirme (dereference) olduğunu — özel bir "dizi erişimi" komutu olmadığını —
doğruluyor. Bu, dil düzeyinde bir eşdeğerliktir: iki yazım da aynı adresi hesaplar ve
aynı değeri verir. Derleyicinin ürettiği makine kodunda kaç adım harcandığı ayrı bir
sorudur — `dizi` bir dizi adı, `p` ise bellekten okunması gereken bir değişkendir — ve
bu ders onu ölçmüyor.

Bu eşdeğerlik, dizinin kendisi ile dizinin **dönüştüğü** işaretçi arasında ince bir
fark bırakıyor: `dizi` adı kendi başına, dizinin tamamının adını taşırken, `p` bir
işaretçi değişkenidir ve başka bir diziyi göstermek üzere yeniden atanabilir — `p = baska_dizi;`
geçerlidir, ama `dizi = baska_dizi;` geçerli değildir, çünkü `dizi` bir dizi
bildirimidir, yeniden atanabilir bir işaretçi değişkeni değil. Dizi adı, bir ifadede
geçtiğinde kural olarak ilk elemanının adresine **dönüşür**; `sizeof` ile `&`
işleçlerinin işleneni olmak gibi sayılı durumlar bunun dışındadır. Dönüşen şey bir
adres değeridir, yeniden atanabilir bir işaretçi değişkeni değil.

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

Bir dizinin uzunluğu derlemede bellidir, ama bu bilgi bir işleve **geçmez**. Bir dizi
parametre olarak alan işlev, dizinin kendisini değil yalnız ilk elemanının adresini alır
ve uzunluğu kendi başına bilemez.

```c
// uzunluk.c — dizi uzunlugu bilgisi cagriya gecmez, alan taraf onu ayrica almak zorundadir
#include <stdio.h>
#include <stddef.h>

static int sabit_sayimla(int veri[]) {
    /* bu islev dizinin gercek boyutunu bilmiyor: sayida 4 varsayiyor */
    int toplam = 0;
    for (int i = 0; i < 4; i++) {
        toplam += veri[i];
    }
    return toplam;
}

static int uzunluklu_sayimla(int veri[], size_t uzunluk) {
    int toplam = 0;
    for (size_t i = 0; i < uzunluk; i++) {
        toplam += veri[i];
    }
    return toplam;
}

int main(void) {
    int buyuk[] = {1, 2, 3, 4, 5, 6};
    printf("%s %d\n", "sabit varsayimli toplam", sabit_sayimla(buyuk));
    printf("%s %d\n", "uzunluk verilen toplam", uzunluklu_sayimla(buyuk, 6));

    int kucuk[] = {10, 20, 30};
    printf("%s %d\n", "farkli boyutta uzunluk verilen toplam", uzunluklu_sayimla(kucuk, 3));
    return 0;
}
```

```
sabit varsayimli toplam 10
uzunluk verilen toplam 21
farkli boyutta uzunluk verilen toplam 60
```

`sabit_sayimla`, kaynakta `int veri[]` yazılsa da, çağrıldığında yalnız bir `int *`
alıyor — dizinin gerçek boyutu hakkında hiçbir bilgi taşımıyor. İşlev döngüsünü sabit
`4` sınırıyla yazmış ve `buyuk` dizisinin ilk dört elemanını toplayıp `10` veriyor —
dizinin gerçek toplamı olan `21`den eksik, çünkü son iki eleman hiç görülmüyor.
`uzunluklu_sayimla` ise uzunluğu ayrı bir parametre (`uzunluk`) olarak alıyor ve doğru
toplamı (`21`) üretiyor; aynı işlev `kucuk` gibi farklı boyuttaki bir diziyle çağrıldığında
da doğru sonucu (`60`) veriyor, çünkü döngü sınırını kaynakta sabit yazmak yerine
çağırandan alıyor. Bu, Fonksiyonlar dersinde ölçülen "parametre her zaman kopyadır"
kuralının bir dizi için nasıl işlediğini gösteriyor: kopyalanan şey dizinin kendisi
değil, ilk elemanının adresi — ve bu adresin yanında, dizinin **eleman sayısı** ayrıca
taşınmadıkça kaybolur.

`sabit_sayimla`nın bildiriminde yazılan `int veri[]`, derleyici tarafından `int *veri`
olarak yorumlanır — parametre listesindeki bir dizi bildirimi, C'de her zaman bir
işaretçi bildirimine **dönüştürülür**. Bu yüzden işlev gövdesi içinde `sizeof(veri)`
yazmak dizinin boyutunu vermez, bir işaretçinin boyutunu verirdi; bu kursta hiçbir
boyut değeri basılmadığı için bu ders o hatayı ölçmüyor, ama `sabit_sayimla`nın neden
sabit bir sayı (`4`) varsaymak **zorunda** kaldığını açıklıyor: işlev, kendisine
geçirilen işaretçiden dizinin gerçek uzunluğunu **hiçbir şekilde** çıkaramaz. Bu,
yukarıda ölçülen dizi-işaretçi ilişkisinin doğrudan bir sonucu: bir dizi işleve
geçtiğinde geriye yalnız ilk elemanın adresi kalıyor, dizinin kendisine ait hiçbir
üst bilgi (eleman sayısı gibi) bu adresle birlikte taşınmıyor. Standart kitaplığın
dizgi işlevlerinin (`strlen` dahil) uzunluk bilgisini neden sonlandırıcı bir baytla
kodladığı da aynı kısıttan geliyor: bir işleve yalnız bir başlangıç adresi
geçirilebiliyorsa, verinin nerede bittiğini o veriyle birlikte, bir işaretle taşımak
gerekiyor.

#### Özet

- Bir dizinin eleman sayısı, kaynaktaki bir sabit ifadeden **derlemede** üretilir
  (sabit ifade katlaması); sınıf **derleyici**.
- Bir dizginin uzunluğu hiçbir yerde saklanmaz, sonlandırıcı `\0` baytına ulaşılana
  kadar taranarak **koşumda** hesaplanır; sınıf **koşum**. Bu ders böylece iki sınıfı
  bir arada taşır.
- Sonlandırıcı baytın konumu koşumda değişebilir; aynı bellek alanı, sonlandırıcının
  yerine göre farklı bir dizgi uzunluğu raporlayabilir.
- Dizi-işaretçi ilişkisi adres basılmadan ölçülebilir: eleman değeri (`*(p + i)` ile
  `dizi[i]` aynı), eşitlik ve eleman cinsinden fark yeterlidir.
- Bir dizinin uzunluk bilgisi bir işleve geçmez; alan taraf uzunluğu ayrı bir
  parametre olarak almadıkça, dizinin gerçek boyutunu kendi başına bilemez.
- Bir dizi parametre olarak bildirildiğinde derleyici tarafından bir işaretçiye
  dönüştürülür; dizginin sonlandırıcı bayt sözleşmesi, tam da bu yüzden — yalnız bir
  başlangıç adresiyle çalışmak zorunda kalan işlevlerin uzunluğu veriyle birlikte
  taşıyabilmesi için — vardır.

#### Sonraki Adım

Bu derste ölçülen iki veri türü — dizi ve dizgi — hep tek bir değişkenin sınırları
içinde kaldı. Yapılar, Birlikler ve Numaralandırmalar dersi, birden çok değeri tek bir
adın altında toplayan üç yeni düzeneği ölçüyor: bir yapının alanları nasıl yerleşiyor,
bir birlik aynı belleği nasıl paylaşıyor ve bir numaralandırma sabiti nasıl derleme
zamanı sabitine dönüşüyor. Bu derste bir dizinin uzunluğunun kaynakta sabit bir ifadeden
geldiği ölçüldü; sıradaki derste aynı sabit ifade fikri, bir numaralandırma değerinin
nasıl üretildiğini açıklayacak. Diziler ve dizgiler burada hep tek tip bir değer
dizisi taşıyordu — hepsi `int` ya da hepsi `char`; yapılar ise farklı tiplerden
değerleri tek bir ad altında toplayacak ve o değerlerin bellekte nasıl yerleştiği de
yine derlemede karara bağlanan bir soru olacak.

### Yapılar, Birlikler ve Numaralandırmalar

Source: https://academia.sh/tr/kurslar/c-temelleri/yapilar-birlikler-ve-numaralandirmalar

Yapı alanlarının derlemede yerleşmesi, bir birliğin aynı belleği paylaşması ve numaralandırma sabitlerinin derleme zamanı sabiti olması; birlikte hangi alanın geçerli olduğunu dilin izlemediğinin bir etiket alanıyla gösterilmesi.

Diziler ve Dizgiler dersi, aynı tipten değerlerin bir dizi içinde nasıl sıralandığını
ölçtü. Bu ders farklı tiplerden değerlerin tek bir ad altında nasıl toplandığını ölçüyor:
bir **yapı** farklı tipleri yan yana yerleştirir, bir **birlik** aynı belleği farklı
yorumlarla paylaştırır, bir **numaralandırma** ise adlandırılmış sabitler üretir — üçü de
kaynakta yazılan bildirimden, derlemede karara bağlanan düzenekler.

Programlama Temelleri kursu (M01/K02) kayıt benzeri veri gruplamasını Python'la zaten
kurmuştu; orada alanlar bir sözlük ya da nesne içinde, çalışma zamanında eklenip
çıkarılabilen adlardı. C'nin karşılığı burada ölçülüyor: bir yapının hangi alanları
taşıyacağı, bu alanların bellekte hangi sırayla duracağı ve bir numaralandırma sabitinin
değeri — hepsi kaynakta yazılan bildirimden, program hiç çalışmadan derlemede
sabitleniyor. Bu, kursun ölçü ekseninin son durağı: bir kaynağın dört aşamadan geçerek
programa dönüştüğü **C'nin Konumu** dersinde açılan soru, burada üç yeni düzeneğin
hangi aşamada karara bağlandığını sorarak kapanıyor.

#### Yapı: Alanların Derlemede Yerleşmesi

Bir yapı bildirimi, her alanın adını ve tipini kaynakta sabitler; bu bildirimden sonra o
yapı tipindeki her değişken aynı alan düzenini taşır. Diziler ve Dizgiler dersinde
ölçülen bir dizi, aynı tipten değerleri sıralarken, bir yapı farklı tiplerden değerleri
tek bir ad altında toplar — bir noktanın `x` ve `y` koordinatları gibi, birlikte anlam
taşıyan ama farklı olabilecek tiplerden değerler. Bir yapı değişkenini bir başkasına
atamak, bütün alanları tek seferde kopyalar.

```c
// yapi.c — yapinin alanlari derlemede yerlesir, atama butun alanlari kopyalar
#include <stdio.h>

struct Nokta {
    int x;
    int y;
};

int main(void) {
    struct Nokta a = {3, 4};
    struct Nokta b = a;
    b.x = 99;

    printf("%s %d\n", "a.x", a.x);
    printf("%s %d\n", "a.y", a.y);
    printf("%s %d\n", "b.x", b.x);
    printf("%s %d\n", "b.y", b.y);
    return 0;
}
```

```
a.x 3
a.y 4
b.x 99
b.y 4
```

**TS12.** `struct Nokta` bildirimi, her `Nokta` değişkeninin bir `x` ve bir `y` alanı
taşıyacağını, bu alanların adlarını ve tiplerini derlemede sabitliyor — bu yapı hangi
alanları taşıyacağını koşumda değil, kaynakta belirliyor. `b = a` ataması, `a`nın her iki
alanını da `b`ye kopyalıyor; ardından `b.x`e yapılan değişiklik yalnız `b`nin kendi
kopyasını etkiliyor, `a.x` hâlâ `3`. Bu, önceki derste ölçülen parametre kopyalama
kuralının bir yapı için nasıl işlediğini gösteriyor: bir yapı değişkeni bir başkasına
atandığında ya da bir işleve geçirildiğinde, aktarılan şey bütün alanların bir
**kopyasıdır** — dizinin aksine, yapı işleve geçtiğinde ilk elemanının adresine
dönüşmez, tüm alanlarıyla kopyalanır.

Alanların bellekteki sırası da kaynaktaki bildirim sırasıyla belirlenir: `struct
Nokta`nın `x` alanı her zaman `y`den önce yerleşir, çünkü bildirimde önce yazılan
alan önce gelir. Bir yapı, kendi içine başka bir yapıyı da alan bulundurabilir —
`struct Nokta` alanları taşıyan bir `struct Cizgi { struct Nokta bas; struct Nokta son;
};` gibi — ve bu iç içe geçme de aynı kuralı katmanlı biçimde sürdürür: her iç yapının
kendi alanları, dış yapının kendi alanı içinde, yine bildirim sırasına göre yerleşir. Bu yerleşim kararı, tıpkı bir dizinin eleman sayısı gibi, program hiç
çalışmadan derleyici tarafından sabitlenir — hangi alanın hangi adreste durduğu
koşumdaki veriye hiç bakılmadan, yalnız bildirimin kendisinden belirlenir. Bu kursta
hiçbir gerçek adres ya da bayt uzaklığı basılmıyor, ama bildirim sırasının bir sonucu
olması, yapı tanımının neden hem verinin **anlamını** hem bellekteki **düzenini**
aynı anda belirlediğini gösteriyor.

#### Birlik: Aynı Belleğin Paylaşılması

Bir birlik, birden çok alanı **aynı** bellek alanında tanımlar — her alan aynı adrese
karşılık gelir, farklı bir tip yorumuyla. Birliğin kendisi hangi alanın "geçerli"
olduğunu izlemez; bu bilgi, bir **etiket** alanıyla ayrıca taşınmalıdır.

```c
// birlik.c — birligin ayni alani paylasmasi, etiketli okumayla guvenli kullanilir
#include <stdio.h>

enum Tur { TAM_SAYI, ONDALIK };

struct EtiketliDeger {
    enum Tur tur;
    union {
        int tam;
        float ondalikli;
    } deger;
};

static struct EtiketliDeger tam_sayi_uret(int v) {
    struct EtiketliDeger e;
    e.tur = TAM_SAYI;
    e.deger.tam = v;
    return e;
}

static struct EtiketliDeger ondalik_uret(float v) {
    struct EtiketliDeger e;
    e.tur = ONDALIK;
    e.deger.ondalikli = v;
    return e;
}

static void yazdir(struct EtiketliDeger e) {
    if (e.tur == TAM_SAYI) {
        printf("%s %d\n", "tam sayi olarak okundu", e.deger.tam);
    } else {
        printf("%s %.2f\n", "ondalik olarak okundu", e.deger.ondalikli);
    }
}

int main(void) {
    struct EtiketliDeger bir = tam_sayi_uret(42);
    struct EtiketliDeger iki = ondalik_uret(3.5f);
    yazdir(bir);
    yazdir(iki);
    return 0;
}
```

```
tam sayi olarak okundu 42
ondalik olarak okundu 3.50
```

**TS13.** `union` içindeki `tam` ile `ondalikli`, aynı bellek alanını paylaşıyor —
biri yazıldığında öbürünün eski değeri anlamsız hâle geliyor, çünkü ikisi aynı baytları
farklı bir tip merceğinden okuyor. `tam_sayi_uret` yalnız `deger.tam`ı yazıyor,
`ondalik_uret` yalnız `deger.ondalikli`yi; `yazdir` de her zaman `tur` alanına bakıp
**hangi yorumun geçerli olduğunu** ondan öğreniyor ve yalnız o alanı okuyor. Bu disiplin
sayesinde ders hiçbir zaman "yazılmayan" alanı okumuyor — `bir` için hep `tam`, `iki`
için hep `ondalikli` okunuyor. `union EtiketliDeger` (`deger` alanı) kendi başına hangi
üyenin son yazılan olduğunu **bilmez**; bu bilgiyi taşımak `tur` alanının ve o alanı
doğru tutmak kaynak yazarının işi.

Bir birliğin kaplayacağı yer de derlemede sabitlenir: standardın koyduğu ölçü, birliğin
**en büyük alanını** taşımaya yetecek kadar yer ayrılmasıdır — çünkü herhangi bir anda
herhangi bir alan yazılabilir olmalı. Bu kursta hiçbir gerçek boyut basılmadığı için bu
ders bunu ölçmüyor, ama iki bildirimin büyüme biçimi arasındaki farkı belirtmek gerekir:
yapı alanları arka arkaya dizer, birlik üst üste bindirir. Bu yüzden birliğe en
büyüğünden küçük bir alan eklemek toplam yeri genellikle büyütmez; yapıya eklemek ise
alan mevcut dolgunun içine sığmadıkça büyütür. İkisinde de son sözü hizalama söyler:
standart hem yapının hem birliğin sonunda adsız dolgu bulunabileceğini belirtir, bu
yüzden toplam yer alanların boyutlarından doğrudan okunmaz.

#### Numaralandırma: Derleme Zamanı Sabiti

Bir numaralandırma bildirimi, adlandırılmış tam sayı sabitleri üretir; bu sabitler
kaynakta yazılan sabit ifadelerin kullanılabileceği her yerde kullanılabilir — örneğin
bir dizinin uzunluğunu belirlemek için.

```c
// asama.c — numaralandirma sabiti (kesit), derleme zamani sabiti olarak kullanilir
#include <stdio.h>

enum Renk { KIRMIZI = 1, YESIL = 2, MAVI = 4 };

int main(void) {
    printf("%s %d\n", "numaralandirma sabiti", (int)MAVI);

    int renkler[MAVI];
    printf("%s %d\n", "sabitle olusturulan dizi eleman sayisi",
           (int)(sizeof renkler / sizeof renkler[0]));
    return 0;
}
```

```
numaralandirma sabiti 4
sabitle olusturulan dizi eleman sayisi 4
```

`MAVI` değeri, `enum Renk` bildiriminde yazılan `4` sabitinden geliyor ve bu değer
program hiç çalışmadan bellidir. `int renkler[MAVI];` bildirimi bunu doğruluyor: `MAVI`,
Diziler ve Dizgiler dersinde ölçülen sabit ifade katlamasının kabul ettiği türden bir
ifade — bir değişken ya da bir işlev çağrısı olsaydı, dizinin boyutu derlemede
belirlenemezdi. `renkler` dizisinin eleman sayısı, hiçbir gerçek boyut basılmadan, yine
`sizeof` oranıyla ölçülüyor ve `4` çıkıyor — `MAVI` ile birebir aynı sayı, çünkü ikisi de
aynı derleme zamanı sabitinden türüyor.

`enum Renk` bildirimindeki `KIRMIZI = 1, YESIL = 2, MAVI = 4` gibi açık değerler
zorunlu değildir; bir numaralandırma, hiçbir değer yazılmadan da bildirilebilir ve bu
durumda ilk ad `0`dan başlayarak, her ad bir öncekinden bir fazla değer alır. Buradaki
bildirim değerleri açıkça yazıyor, çünkü `KIRMIZI`, `YESIL` ve `MAVI` ayrı bit
konumlarını (`1`, `2`, `4`) temsil ediyor ve bu, renklerin bir bit maskesiyle
birleştirilebilmesini sağlıyor — `KIRMIZI | MAVI` gibi bir ifade, iki rengi aynı anda
temsil eden `5` değerini üretebilir. Bu kullanım da yine derlemede sabitlenen bir
özellik: hangi sabitin hangi bit konumuna karşılık geldiği, kaynakta yazılan `=`
işaretinden geliyor, koşumdaki hiçbir veriye bakılmıyor.

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

Bir birlik, hangi alanının en son yazıldığını **kendi başına** izlemez — bu bilgiyi dil
değil, kaynak yazarı taşımak zorundadır. Yukarıdaki `EtiketliDeger` yapısı bunu bir
etiket alanıyla çözüyor: `tur`, `deger`in hangi üyesinin geçerli olduğunu söylüyor ve
`yazdir` her zaman önce `tur`e bakıyor.

Eğer bu disiplin bırakılsaydı — `yazdir` `tur`e hiç bakmadan, örneğin her zaman
`deger.ondalikli`yi okusaydı — `bir` değişkeni için (ki `tam_sayi_uret` yalnız
`deger.tam`ı yazmıştı) okunan bayt örüntüsü bir `int` olarak yazılmış, bir `float`
olarak okunmuş olurdu. Bu, birliğin garanti ettiği tek şeyin **aynı adresi paylaşmak**
olduğunu, hangi tip yorumunun **anlamlı** olduğunu garanti etmediğini gösteriyor —
yorum uyuşmazlığının sonucu belirsiz bir bit örüntüsüdür ve bu kursun genel kuralınca
kod bloğunda **denenmiyor**. Yanına konan tanımlı karşılık, yukarıdaki etiketli
okumanın kendisi: her okuma, o birliğe en son hangi üyenin yazıldığını bağımsız bir
alandan (`tur`) öğrenip yalnız o üyeyi okuyor ve bu ölçüm üç kez koşturulduğunda da
aynı iki satırı üretiyor.

Etiketli okumanın çalışması için, etiketin **her zaman** ilgili yazmayla birlikte
güncellenmesi gerekiyor — `tam_sayi_uret` ve `ondalik_uret` işlevlerinin ikisi de,
kendi alanlarını yazmadan önce `tur`ü ayarlıyor. Eğer bir üçüncü işlev `deger.tam`ı
yazıp `tur`ü güncellemeyi unutsaydı, o değişkeni okuyan `yazdir` hâlâ eski `tur`
değerine bakar ve yanlış üyeyi okurdu — birlik kendi başına bu tutarsızlığı ne
üretebilir ne engelleyebilir, çünkü birliğin kendisi yalnız bellek paylaşımını sağlıyor,
etiketle senkron kalmayı değil. Bu, C'nin birlik düzeneğinin bilinçli bir tasarım
kararını yansıtıyor: dil, alan seçimini izlemenin **maliyetini** üstlenmiyor — her
yazmada hangi alanın aktif olduğunu ayrıca kaydetmiyor — ve bu sorumluluğu, tıpkı dizi
uzunluğu bilgisinde olduğu gibi, tamamen kaynak yazarına bırakıyor.

Bu, yapı ile birliğin birbirini tamamlayan iki düzenek olduğunu da gösteriyor: yapı her
alan için **ayrı** bellek ayırıp hepsini aynı anda geçerli kılarken, birlik tek bir
bellek alanını **paylaştırıp** yalnız birini geçerli kılıyor. `EtiketliDeger`, bu ikisini
birleştiriyor — dıştaki yapı hem etiketi hem paylaşılan alanı taşıyor ve etiket alanı
sayesinde birliğin izlemediği bilgi yapı düzeyinde tutulmuş oluyor.

Bu birleşim, C'de "etiketli birlik" (tagged union) diye anılan yaygın bir kalıbın
temelini oluşturuyor: birden çok olası biçimden yalnız birini taşıyan bir değeri
modellemek isteyen bir kaynak yazarı, bu üç düzeneği — yapı, birlik ve numaralandırma —
birlikte kullanır. Numaralandırma, hangi biçimin geçerli olduğunu adlandırılmış
sabitlerle ifade eder; birlik, o biçimlerin ortak bir bellek alanını paylaşmasını
sağlar; yapı, ikisini bir arada, tek bir değer olarak taşır. Bu derste ölçülen üç
düzenek, bu yüzden birbirinden bağımsız üç konu değil, aynı kalıbın üç bileşeni.

#### Özet

- Bir yapının alanları, adları ve tipleriyle birlikte **derlemede** sabitlenir; bir
  yapı değişkeni bir başkasına atandığında bütün alanlar tek seferde kopyalanır.
- Bir birlik, birden çok alanı aynı bellek adresinde tanımlar; hangi alanın son
  yazılan olduğunu dil izlemez, bu bilgiyi kaynak yazarı ayrıca (bir etiket alanıyla)
  taşımak zorundadır.
- Numaralandırma sabitleri (`enum Renk` içindeki `MAVI` gibi) derleme zamanı
  sabitleridir; bir dizinin uzunluğunu belirlemek gibi sabit ifade gerektiren her
  yerde kullanılabilirler — sınıf **derleyici**.
- Bir birlikte yazılmayan bir üyeyi okumak (etiketsiz, yanlış tip varsayımıyla)
  baytları başka bir tip olarak yeniden yorumlar; C bunu yasaklamaz ama son yazılan
  üyeye karşılık gelmeyen baytların değeri **belirtilmemiştir** ve okunan örüntü bir
  tuzak temsiliyse davranış tanımsız olur. Bu kursta koşturulmaz; tanımlı karşılık,
  etiketli okumadır.
- Yapı her alanı ayrı bellekte tutup hepsini birden geçerli kılarken, birlik tek bir
  bellek alanını paylaştırıp yalnız birini geçerli kılar — ikisi birbirini tamamlayan
  iki düzenek.
- Numaralandırma, birlik ve yapı bir arada kullanıldığında "etiketli birlik" kalıbını
  kurar: numaralandırma hangi biçimin geçerli olduğunu adlandırır, birlik biçimlerin
  belleğini paylaştırır, yapı ikisini tek bir değerde birleştirir.

#### Sonraki Adım

Bu konuda ölçülen her karar, bir programın **tek bir koşumu** içinde kaldı: bir tipin
aralığı, bir adın kapsamı, bir işlecin seçimi, bir dalın izlediği yol, bir çağrının
bağlandığı gövde, bir dizinin uzunluğu, bir birliğin geçerli alanı — hepsi ya kaynaktan
ya da o koşumun kendi verisinden okunabiliyordu. Girdi/Çıktı dersi bu zemine son bir
soru ekliyor: bir programın **dışarıdan** aldığı ya da dışarıya yazdığı veri, hangi
aşamada yorumlanıyor? Bir biçim dizesindeki (`format string`) her yer tutucu, karşılık
gelen bağımsız değişkenin tipiyle uyuşmak zorunda, ama bu uyuşmayı derleyici yalnız
belirli koşullarda yakalayabiliyor — dilin kendisi bunu garanti etmiyor. Bu derste
ölçülen `EtiketliDeger` örneğinde etiketi doğru tutmak kaynak yazarının işiydi; sıradaki
derste biçim dizesini argümanlarla uyumlu tutmak da aynı şekilde kaynak yazarının
sorumluluğunda olacak.

Bu konuda ölçülen yedi ders, dört sınıfın (ön işlemci, derleyici, bağlayıcı, koşum) her
birine en az bir kez uğradı: Veri Tipleri ve İşleçler derleyicide, Denetim Akışı
koşumda, Değişkenler ile Fonksiyonlar bağlayıcıda, Diziler ve Dizgiler ikisinde birden
ve bu ders yine derleyicide durdu. Kapanış dersi, bu yedi dersin ölçtüğü kararları tek
bir tabloda topluyor ve girdi çıktının kendi payını — biçim dizelerinin koşumda
yorumlanmasını — bu tabloya ekliyor.

### Girdi/Çıktı

Source: https://academia.sh/tr/kurslar/c-temelleri/girdi-cikti

Bir biçim dizesinin metni derlemede bellidir ama ne anlama geldiği ancak koşumda belli olur: dokuz gözlemin biri ön işlemcide, ikisi derleyicide, biri bağlayıcıda, beşi koşumda karara bağlanıyor. Sınırlayıcı ölçüm, biçim dizesi ile argümanların uyumunu dilin kendisinin garanti etmediğini gösteriyor.

Kurs boyunca sorulan soru hep aynıydı: bu karar hangi aşamada veriliyor. Girdi ve çıktı, o
soruyu tek bir satırda dört aşamaya birden dağıtabildiği için kursun kapanışına uygun düşüyor.
Bir yazma çağrısında biçim dizesindeki bir makro **ön işlemcide** yerine konur, dizenin metni
**derlemede** sabitlenir, çağrılan işlevin tanımı **bağlamada** bulunur ve dizenin ne anlama
geldiği ancak **koşumda** belli olur.

Bu ders o dağılımı ölçüyor. Ölçüm çekirdeğin `veriden hesaplanan deger` satırının bıraktığı
yerden devam ediyor: orada bir toplam koşumda hesaplanmıştı, burada o toplamın **nasıl
yazıldığı** soruluyor.

#### Dokuz Gözlem

```c
// girdicikti.c — bir bicim dizesinin hangi asamada karara baglandigi
#include <stdio.h>
#include <string.h>

#define ETIKET "olcum"

static void gozle(const char *ad, const char *asama, const char *kanit) {
    printf("%-34s%-12s%s\n", ad, asama, kanit);
}

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

    printf("%-34s%-12s%s\n", "gozlem", "asama", "kanit");

    /* on islemci */
    snprintf(tampon, sizeof tampon, "%s basligi metne kondu", ETIKET);
    gozle("bicim dizesindeki makro", "on islemci", tampon);

    /* derleyici */
    snprintf(tampon, sizeof tampon, "eleman sayisi %d", (int)(sizeof tampon / sizeof tampon[0]));
    gozle("tampon uzunlugu", "derleyici", tampon);

    const char *sabit = "deger: %d";
    snprintf(tampon, sizeof tampon, "metin %d karakter", (int)strlen(sabit));
    gozle("bicim dizesinin metni", "derleyici", tampon);

    /* baglayici */
    gozle("bicimleme islevinin tanimi", "baglayici", "kaynakta tanim yok, cagri yine de cozuldu");

    /* kosum */
    int deger = 42;
    const char *bicim = (deger < 100) ? "%d" : "[%d]";
    snprintf(tampon, sizeof tampon, bicim, deger);
    gozle("bicim dizesinin secimi", "kosum", tampon);

    int genislik = (int)strlen("dort") + 1;
    snprintf(tampon, sizeof tampon, "%*d", genislik, deger);
    gozle("alan genisligi argumani", "kosum", tampon);

    int istenen = snprintf(kucuk, sizeof kucuk, "%s", "merhaba dunya");
    snprintf(tampon, sizeof tampon, "istenen %d yazilan %d", istenen, (int)strlen(kucuk));
    gozle("kesilen yazim", "kosum", tampon);

    int okunan = 0, sayi = 0;
    okunan = sscanf("sicaklik 21 derece", "sicaklik %d", &sayi);
    snprintf(tampon, sizeof tampon, "atanan alan %d deger %d", okunan, sayi);
    gozle("metinden okuma", "kosum", tampon);

    int eslesmeyen = sscanf("sicaklik yirmibir", "sicaklik %d", &sayi);
    snprintf(tampon, sizeof tampon, "atanan alan %d", eslesmeyen);
    gozle("eslesmeyen okuma", "kosum", tampon);

    printf("\n%-12s%d\n", "on islemci", 1);
    printf("%-12s%d\n", "derleyici", 2);
    printf("%-12s%d\n", "baglayici", 1);
    printf("%-12s%d\n", "kosum", 5);
    printf("toplam gozlem: %d\n", 9);
    return 0;
}
```

```
gozlem                            asama       kanit
bicim dizesindeki makro           on islemci  olcum basligi metne kondu
tampon uzunlugu                   derleyici   eleman sayisi 64
bicim dizesinin metni             derleyici   metin 9 karakter
bicimleme islevinin tanimi        baglayici   kaynakta tanim yok, cagri yine de cozuldu
bicim dizesinin secimi            kosum       42
alan genisligi argumani           kosum          42
kesilen yazim                     kosum       istenen 13 yazilan 7
metinden okuma                    kosum       atanan alan 1 deger 21
eslesmeyen okuma                  kosum       atanan alan 0

on islemci  1
derleyici   2
baglayici   1
kosum       5
toplam gozlem: 9
```

**TS14.** `ETIKET` makrosu biçim dizesine derleyici kaynağı görmeden önce kondu. Ön işlemci
için bu bir metin birleştirmesidir; dizenin bir biçim dizesi olduğunu bilmez, `%s` ile
`%d` arasındaki farkı da bilmez. Bu yüzden makroyla kurulan bir biçim dizesi, argüman
listesiyle uyuşmasa bile ön işlemci aşamasını sorunsuz geçer.

**TS15.** Tampon uzunluğu derlemede bellidir: `sizeof tampon / sizeof tampon[0]` bir sabit
ifadedir ve `64` verir. Dizinin uzunluğunun derlemede bilindiği **Diziler ve Dizgiler**
dersinde ölçülmüştü; burada aynı bilgi bir yazma çağrısının **sınırı** olarak kullanılıyor.
Biçim dizesinin metni de aynı sınıfa düşer — `deger: %d` dokuz karakterdir ve bu sayı
kaynaktan okunur, koşumdan değil.

**TS16.** Biçimleme işlevinin tanımı kaynakta yok. Yalnız başlık dosyasından gelen bildirimi
var ve çağrı buna rağmen çözüldü: tanımı **bağlayıcı** buldu. Bu, **Fonksiyonlar** dersindeki
`baska birimdeki islev` satırının aynısıdır; tek fark, tanımın bu kez ayrı bir kaynak
biriminde değil, dilin standart kitaplığında olmasıdır.

**TS17.** Kalan beş gözlem koşuma düşüyor ve hepsinin ortak yanı şu: kaynak metni değişmeden
sonucun değişebilmesi. Biçim dizesi bir koşula göre seçildi (`deger < 100` doğru olduğu için
köşeli ayraçsız biçim kullanıldı), alan genişliği bir argümandan geldi (`dort` beş karakterlik
bir alan açtı), yazma kesildi, bir metinden bir sayı okundu ve bir okuma **hiçbir alan
atayamadı**.

#### Dönüş Değeri Ne Söylüyor

**TS18.** Kesilen yazım satırı, dönüş değerinin ne olduğunu gösteriyor: `istenen 13 yazilan 7`.
Yazma işlevi, tampona sığdırabildiği karakter sayısını değil, **sığdırmak isteseydi kaç
karakter yazacağını** döndürüyor. On üç karakterlik metin sekiz baytlık tampona konduğunda
yedi karakter ve bir sonlandırıcı yazıldı, ama dönüş değeri on üç kaldı. Bu iki sayının
ayrışması, kesilmenin olup olmadığını anlamanın tek belirlenimci yoludur: dönüş değeri tampon
uzunluğundan küçükse yazım tamdır.

**TS19.** Okuma tarafında dönüş değeri başka bir şey sayıyor: **kaç alanın atandığını**. Aynı
biçim dizesi bir metinde bir alan atadı, ötekinde sıfır. İkinci metin de geçerli bir metindi
ve program çökmedi — yalnız `sayi` değişkeni **dokunulmamış** kaldı. Okumanın başarısızlığı
bir hata değeriyle değil, bir **sayımla** bildiriliyor ve o sayıyı okumayan bir kaynak,
atanmamış bir değişkeni atanmış sanır.

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

**TS20.** Biçim dizesi ile argümanların uyumunu **dilin kendisi garanti etmez**. Yukarıdaki
ölçümde biçim dizesi bir yerde bir değişkenden geldi — koşula göre seçilen `bicim` işaretçisi.
O çağrıda derleyicinin elinde, dizenin içeriğine bakarak argümanları denetleyecek bir bilgi
yoktu; dize koşumda seçiliyordu. Biçim dizesi **kaynakta yazılı bir sabit olduğunda**
derleyicinin elinde uyumu denetleyecek bilgi vardır ve yaygın derleyiciler bunu isteğe bağlı
bir uyarı olarak sunar. Denetimin kendisi dilin bir garantisi değildir: standart, argümanın
biçim belirleyicisiyle uyuşmamasını doğrudan tanımsız davranış sayar ve hiçbir denetim
öngörmez. Dize bir değişkenden geldiğinde o bilgi de kalmaz — denetim **düşer** ve sorumluluk
tümüyle yazana geçer.

Bu, kursun dört aşamalı sorusunun en keskin sonucudur: aynı çağrı, biçim dizesinin nereden
geldiğine göre **derleyici** sınıfından **koşum** sınıfına kayar. Kaynak metni neredeyse
aynıdır; değişen tek şey, bir bilginin hangi aşamada elde bulunduğudur.

#### Kapanış Tablosu

On üç ders boyunca ölçülen kararlar aşağıda toplanıyor. Aşama hücresi her dersin kendi
metninde yazılı olandan alınmıştır; birden çok aşama ölçen dersler ikisini de taşır.

| Ders | Ölçülen karar | Aşama | Sınırlayıcı ölçüm |
|---|---|---|---|
| C'nin Konumu | Çekirdeğin on dört gözleminin dağılımı | dördü birden | Dört aşama bir karmaşıklık değil, dört ayrı denetim noktasıdır |
| Derleme Aşamaları | Her aşamadan birer satırın yan yana koşumu | dördü birden | Aşamalar sırayla çalışır ve geri dönmez; ön işlemcinin ürettiği metni derleyici düzeltmez |
| Başlık Dosyaları | Bildirimin metin olarak kopyalanması | ön işlemci · bağlayıcı | Başlık dosyası bir sözleşme değildir; bildirimle tanımın uyumunu dil denetlemez |
| Ön İşlemci | Makronun tip bilmeden metin koyması | ön işlemci | Makro bir işlev değildir; argümanı birden çok kez yerine konabilir |
| Derleyici Uyarıları ve Standartlar | Uyarının hangi aşamada doğduğu | derleyici | Uyarı bir hata değildir ve uyarısız derlenen bir program doğru sayılmaz |
| Veri Tipleri | Tip aralığı ve tam sayı yükseltmesi | derleyici | Taşma davranışı tipin işaretine bağlıdır: işaretsizde tanımlı, işaretlide tanımsız |
| Değişkenler, Kapsam ve Ömür | Adın görünürlüğü ile değerin yaşadığı süre | derleyici · koşum · bağlayıcı | Aynı ad iki birimde iki ayrı nesne olabilir; bunu belirleyen bağlayıcıdır |
| İşleçler | İşlecin tipe göre anlam değiştirmesi | derleyici | Öncelik gruplamayı belirler, değerlendirme sırasını her zaman belirlemez |
| Denetim Akışı | Hangi dalın izlendiği | koşum · ön işlemci | Hangi dalların var olduğu koşumla değil, ön işlemciyle belirlenmiş olabilir |
| Fonksiyonlar | Çağrının tanıma bağlanması | bağlayıcı | Bildirimle tanımın birimler arası uyuşmazlığını ne derleyici ne bağlayıcı denetler |
| Diziler ve Dizgiler | Dizi uzunluğu ile dizgi uzunluğu | derleyici · koşum | Dizinin uzunluğu derlemede bellidir ama bu bilgi işleve geçmez |
| Yapılar, Birlikler ve Numaralandırmalar | Alanların yerleşimi ve sabitlerin değeri | derleyici | Birlik, hangi alanın en son yazıldığını kendi başına izlemez |
| Girdi/Çıktı | Biçim dizesinin yorumlanması | koşum | Biçim dizesi bir değişkenden geldiğinde derleyicinin uyum denetimi düşer |

#### Özet

- Bir yazma çağrısı dört aşamayı birden taşıyabiliyor: makro **ön işlemcide**, tampon uzunluğu
  ve dizenin metni **derleyicide**, işlevin tanımı **bağlayıcıda**, dizenin anlamı **koşumda**
  karara bağlandı.
- Dokuz gözlemin biri ön işlemcide, ikisi derleyicide, biri bağlayıcıda, beşi koşumda
  ölçüldü; girdi çıktı, kursun dört sınıfının en yoğun karıştığı yerdir.
- Yazma işlevinin dönüş değeri yazılan karakter sayısı değil, **yazılmak istenen** sayıdır:
  on üç istendi, yedi yazıldı, dönüş on üç kaldı.
- Okuma işlevinin dönüş değeri **atanan alan sayısıdır**; sıfır dönen bir okumada hedef
  değişken dokunulmamış kalır ve bunu fark etmenin tek yolu o sayıyı okumaktır.
- Biçim dizesi kaynakta sabitken derleyici argüman uyumunu denetleyebiliyor; dize bir
  değişkenden geldiğinde aynı çağrı **koşum** sınıfına kayıyor ve denetim düşüyor.

#### Kurs Kapanışı

On üç ders boyunca tek bir soru soruldu: bu karar hangi aşamada veriliyor. Ortak tanımın on
dört gözlemi **4 ön işlemci, 4 derleyici, 3 bağlayıcı, 3 koşum** olarak dağılmıştı ve bu
dağılım kurs boyunca değişmedi — değişen, hangi düzeneğin hangi sınıfa düştüğüydü.

Dil ve Derleme konusu soruyu kaynağın programa dönüşme yoluna sordu: dört aşamanın varlığına,
bildirimlerin metin olarak paylaşılmasına, ön işlemcinin tip bilmemesine ve uyarının nerede
doğduğuna. Temel Sözdizim konusu aynı soruyu dilin kendi yapılarına taşıdı: bir tipin taşıdığı
değere, bir adın görünürlüğü ile bir değerin ömrünün ayrı sorular olmasına, bir işlecin tipe
göre anlam değiştirmesine, bir çağrının nerede çözüldüğüne, bir uzunluğun ne zaman bilindiğine
ve bir kayıt tipinin alanlarının nasıl yerleştiğine.

Kursun en çok tekrarlanan tek bulgusu şudur: **aynı kaynak metni, bir bilginin hangi aşamada
elde bulunduğuna göre farklı davranır.** Bir dizi uzunluğu derlemede bilinir, bir dizgi
uzunluğu koşumda; bir makro tip bilmez, bir derleyici bilir; bir ad iki birimde aynı yazılır
ama aynı nesne olmayabilir.

Sıradaki kurs belleğe ve işaretçilere bakıyor: bir değerin nerede durduğu, kim tarafından
ayrıldığı ve ne zaman bırakıldığı. C'de bu soruların hepsi kaynakta yazılıdır ve hiçbiri
kendiliğinden denetlenmez.

