---
title: Fonksiyonlar
source: 'https://academia.sh/tr/kurslar/c-temelleri/fonksiyonlar'
course: 'C Temelleri'
language: tr
updated: '2026-09-13T05:07:32+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir çağrının başka birimdeki tanıma bağlanmasının bağlayıcı aşamasında çözüldüğünü ölçer'
    - 'Bildirim ile tanımın ayrılmasını ve parametre aktarımının her zaman kopya olduğunu gösterir'
    - 'Dönüş değerinin tipinin çağıranın yorumunu nasıl belirlediğini örnekle ayırt eder'
---

# 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ğı.
