# C Programlama Temelleri

> Algoritma kurmadan bellek yönetimine, işaretçilerden dosya işlemlerine ve önişlemciye kadar C programlamanın temelini oluşturan ayrıntılı ders notları.

- Author: Muhammet Ali Köker
- Language: tr
- Canonical: https://alikoker.com.tr/c-programlama-temelleri
- Translation: https://alikoker.com.tr/en/c-programming-fundamentals
- Published: 2014-01-11T17:40:00+03:00
- Modified: 2026-07-14T18:10:00+03:00
- Verified: 2026-08-08T15:00:00+03:00
- Type: article

C ile ilgili bu notları programlamaya giriş ve sistem programlama temellerini çalışırken oluşturdum. İlk sürüm 2013-2014 döneminin ders akışını izliyordu. Daha sonraki düzenlemelerde dil standardındaki değişiklikleri yalnız gerekli yerlerde güncelleme notu olarak ekledim; C'nin temel semantiğini güncel kolaylıklarla geriye doğru yeniden yazmadım.

## Ünite 1: Programlamaya Giriş

### Problem çözme

Programlama, bir problemi bilgisayarın yürütebileceği kesin adımlara dönüştürme sürecidir. Kod yazmak bu sürecin son aşamalarından biridir. Önce problem anlaşılır, girdiler ve çıktılar belirlenir, çözüm yöntemi oluşturulur ve ancak bundan sonra programlama diline geçilir.

Temel süreç:

```text
Problemi tanımla
    ↓
Girdi ve çıktıları belirle
    ↓
Algoritmayı oluştur
    ↓
Veri yapılarını seç
    ↓
Programı yaz
    ↓
Derle ve çalıştır
    ↓
Test et
    ↓
Sonucu değerlendir
```

Bir programın doğru çalışması yalnız sözdiziminin doğru olmasına bağlı değildir. Program:

- doğru problemi çözmeli,
- bütün gerekli durumları ele almalı,
- hatalı girdilere karşı belirlenen davranışı göstermeli,
- kaynakları uygun kullanmalı,
- bakım yapılabilecek kadar açık olmalıdır.

### Algoritma

Algoritma, belirli girdilerden istenen çıktıyı elde etmek için tanımlanmış, açık ve sonlu işlem dizisidir.

Bir algoritmanın temel özellikleri:

- adımları belirsiz olmamalıdır,
- uygulanabilir işlemlerden oluşmalıdır,
- sonlu bir noktada tamamlanmalıdır,
- aynı koşullarda tanımlı bir davranış göstermelidir,
- problemin gerekli bütün durumlarını kapsamalıdır.

Örneğin üç sayıdan en büyüğünü bulmak için:

```text
enBuyuk <- a

eğer b > enBuyuk ise
    enBuyuk <- b

eğer c > enBuyuk ise
    enBuyuk <- c

enBuyuk değerini döndür
```

Bu algoritma C veya C++'tan bağımsızdır. Programlama dili yalnız algoritmanın gerçeklenme aracıdır.

### Sözde kod

Sözde kod, algoritmayı belirli bir dilin ayrıntılarına bağlamadan ifade eder.

Örnek:

```text
ortalama(liste):
    toplam <- 0

    listedeki her eleman için:
        toplam <- toplam + eleman

    döndür toplam / eleman_sayısı
```

Sözde kod:

- kontrol akışını,
- veri ilişkilerini,
- işlem sırasını

göstermelidir. Derlenebilir olmak zorunda değildir.

### Atama

Atama:

```text
x <- 5
```

veya programlama dilinde:

```c
x = 5;
```

biçiminde ifade edilir.

Buradaki `=` matematikteki eşitlik işareti değildir. Sağ taraftaki ifade değerlendirilir ve sonuç sol taraftaki nesneye atanır.

Bu nedenle:

```c
x = x + 1;
```

programlama açısından anlamlıdır. Önce eski `x` okunur, bir artırılır ve yeni değer tekrar `x` içine yazılır.

### Koşul

Bir işlemin yalnız belirli koşulda yapılması seçim yapısıdır.

```text
eğer sıcaklık < 0 ise
    "donma riski" yaz
değilse
    "donma riski yok" yaz
```

C ve C++ karşılığı:

```c
if (temperature < 0) {
    /* işlem */
} else {
    /* diğer işlem */
}
```

### Döngü

Belirli işlemlerin tekrarlanması döngüyle ifade edilir.

Koşul başta denetleniyorsa:

```text
while koşul:
    işlem
```

Belirli sayıda tekrar için:

```text
for başlangıç; koşul; güncelleme:
    işlem
```

Koşulun döngü sonunda denetlendiği yapı:

```text
do:
    işlem
while koşul
```

C ve C++ bu üç temel modeli de destekler.

### Fonksiyon

Fonksiyon belirli bir görevi kapsülleyen, adlandırılmış program parçasıdır.

Örnek:

```c
int max2(int a, int b)
{
    return a > b ? a : b;
}
```

Fonksiyon kullanımı:

- tekrar eden kodu azaltır,
- programı parçalara ayırır,
- test edilebilirliği artırır,
- arabirim ile gerçekleştirim ayrımını destekler.

### Akış şemaları

Akış şeması algoritmayı grafiksel olarak gösterir.

Temel semboller:

- başlangıç ve bitiş,
- işlem,
- karar,
- giriş ve çıkış,
- akış oku.

Küçük algoritmalarda öğretici olabilir. Büyük yazılımlarda sözde kod, durum diyagramları, UML ve doğrudan kaynak kod daha kullanışlı hale gelir.

Nassi-Shneiderman diyagramları yapısal programlamanın:

- sıra,
- seçim,
- tekrar

yapılarını kutular içinde gösterir. Tarihsel ve öğretici değeri vardır, ancak modern yazılım geliştirmede zorunlu bir araç değildir.

### Programlama dilleri

Programlama dilleri farklı soyutlama düzeyleri sunar.

C:

- düşük düzey donanım ayrıntılarına erişebilir,
- doğrudan bellek adresleriyle çalışabilir,
- küçük çalışma zamanı maliyetine sahiptir,
- sistem programlamada güçlüdür.

C++:

- C benzeri düşük düzey olanakları,
- sınıflar,
- şablonlar,
- RAII,
- jenerik programlama,
- standart kütüphane,
- değer semantiği,
- modern tür sistemi

ile genişletir.

C++ tarihsel olarak C'den gelişmiş olsa da günümüzde "C'nin üzerine birkaç nesne yönelimli özellik eklenmiş dil" olarak düşünülmemelidir. Kendi dil kuralları, tür sistemi ve standart kütüphanesi olan bağımsız bir programlama dilidir.

## Ünite 2: C Programlarının Yapısı

### En küçük C programı

Standart bir C programı:

```c
int main(void)
{
    return 0;
}
```

ile başlayabilir.

`main`, barındırılan standart C ortamında programın giriş noktasıdır.

Yaygın iki biçim:

```c
int main(void)
```

ve:

```c
int main(int argc, char *argv[])
```

şeklindedir.

`void main()` standart C programının taşınabilir giriş biçimi değildir.

### Derleme süreci

Basitleştirilmiş süreç:

```text
Kaynak kod
   ↓
Önişleme
   ↓
Derleme
   ↓
Assembly
   ↓
Nesne kodu
   ↓
Bağlama
   ↓
Yürütülebilir program
```

Gerçek araç zincirinde bazı aşamalar birleştirilebilir.

Örneğin:

```bash
cc program.c -o program
```

komutu önişleme, derleme, assembly ve bağlama adımlarını tek komutla yürütebilir.

### Başlık dosyaları

Standart kütüphane bildirimleri başlık dosyalarıyla görünür hale getirilir.

Örnek:

```c
#include <stdio.h>
```

`<stdio.h>`:

- `printf`,
- `fopen`,
- `fclose`,
- `fread`,
- `fwrite`

gibi standart giriş çıkış işlevlerinin bildirimlerini içerir.

Standart başlıklar için:

```c
#include <stdio.h>
```

biçimi tercih edilir.

Projeye ait başlıklar için:

```c
#include "my_header.h"
```

kullanımı yaygındır.

### Açıklamalar

C'de çok satırlı açıklama:

```c
/* açıklama */
```

biçimindedir.

C99'dan itibaren tek satırlı açıklama da standarttır:

```c
// açıklama
```

Açıklama kodun ne yaptığını tekrar etmek yerine, gerektiğinde neden o şekilde yazıldığını açıklamalıdır.

### Temel veri türleri

C'nin temel türlerinden bazıları:

```text
char
short
int
long
long long
float
double
long double
_Bool / bool
```

C23 ile `bool`, `true` ve `false` doğrudan dil düzeyindeki adlar arasında yer alır.

Tamsayı türlerinin kesin bit genişliği her platformda aynı olmak zorunda değildir.

Örneğin:

```c
sizeof(int)
```

değeri çoğu güncel sistemde 4 bayttır, ancak C standardı `int` türünün daima 32 bit olmasını şart koşmaz.

Kesin genişliğin önemli olduğu durumlarda:

```c
#include <stdint.h>

int32_t
uint32_t
int64_t
uint64_t
```

gibi türler, gerçekleştirim tarafından sağlanıyorsa kullanılabilir.

### sizeof

Bir türün veya nesnenin bayt cinsinden boyutu:

```c
sizeof(int)
sizeof variable
```

ile alınır.

`sizeof(char)` tanım gereği 1'dir. Ancak bir C baytının bit sayısı:

```c
CHAR_BIT
```

ile belirlenir ve standart yalnız bunun en az 8 olmasını garanti eder.

### Değişken bildirimi

```c
int count;
double average;
char grade;
```

Başlangıç değeri:

```c
int count = 0;
double pi = 3.141592653589793;
```

Yerel otomatik değişken başlangıç değeri verilmeden okunmamalıdır.

### Sabitler

Literal:

```c
42
3.14
'A'
"metin"
```

gibi kaynak kodda doğrudan yazılan değerlerdir.

Salt okunur nesne:

```c
const int max_count = 100;
```

ile tanımlanabilir.

C'de `const` ile tanımlanan nesne değiştirilemez olarak kullanılır, ancak her bağlamda derleme zamanı sabiti anlamına gelmez.

Makro sabit:

```c
#define MAX_COUNT 100
```

şeklinde de tanımlanabilir.

Tür güvenliği ve hata ayıklama açısından uygun olduğunda `const`, `enum` veya C23'teki sabit ifade olanakları makro yerine tercih edilebilir.

### Yerel değişken

Blok içinde tanımlanan değişkenin kapsamı ilgili blokla sınırlıdır:

```c
void f(void)
{
    int x = 10;
}
```

`x` bu blok dışından adıyla erişilemez.

### Küresel değişken

Dosya kapsamında tanımlanan nesne:

```c
int counter;
```

dosya kapsamına sahiptir.

Küresel durum program parçaları arasında gizli bağımlılık oluşturabileceğinden gerektiğinde kullanılmalıdır.

### static

Blok içindeki:

```c
static int count;
```

nesnesinin saklama süresi programın tamamıdır, fakat adı ilgili blokla sınırlıdır.

Dosya kapsamındaki:

```c
static int value;
```

iç bağlantıya sahiptir ve başka çeviri birimlerinden aynı adla erişilemez.

`static` sözcüğünün etkisi bulunduğu bağlama göre değerlendirilmelidir.

### extern

Başka bir yerde tanımlanmış dış bağlantılı nesneyi bildirmek için:

```c
extern int global_counter;
```

kullanılabilir.

Büyük programlarda ortak değişkenleri başlık dosyalarında `extern` ile bildirmek, tek bir `.c` dosyasında tanımlamak tipik yaklaşımdır.

### register

Tarihsel C'de:

```c
register int i;
```

derleyiciye değişkenin hızlı erişim için yazmaçta tutulabileceğine ilişkin bir ipucu verirdi.

Modern derleyiciler yazmaç atamasını optimizasyon sırasında kendileri yaptığı için bu kullanım pratik önemini büyük ölçüde kaybetmiştir. Güncel C'de kaynak uyumluluğu açısından görülebilir ancak performans yönetimi için güvenilir bir araç değildir.

## Ünite 3: C'de İşleçler ve Program Denetimi

### Aritmetik işleçler

```text
+   toplama
-   çıkarma
*   çarpma
/   bölme
%   kalan
```

Tamsayı bölmesi kesirli kısmı atar:

```c
7 / 2
```

sonucu:

```text
3
```

olur.

Kayan noktalı sonuç için operandlardan en az biri kayan nokta olmalıdır:

```c
7.0 / 2.0
```

sonucu:

```text
3.5
```

olur.

### Artırma ve azaltma

```c
++i;
--i;
i++;
i--;
```

Ön ve son biçimlerin ifade içindeki değer üretme zamanı farklıdır.

Basit sayaç güncellemelerinde:

```c
++i;
```

ve:

```c
i++;
```

aynı son değeri oluşturur.

Bir ifadede aynı nesneyi birden fazla kez, sıralaması güvence altına alınmadan değiştiren karmaşık kullanımlardan kaçınılmalıdır.

### Bileşik atama

```c
x += 5;
x -= 2;
x *= 3;
x /= 4;
```

genellikle:

```c
x = x + 5;
```

gibi ifadelerin kısa biçimidir.

### Karşılaştırma işleçleri

```text
==  eşit
!=  eşit değil
<   küçük
>   büyük
<=  küçük veya eşit
>=  büyük veya eşit
```

Atama ile karşılaştırma farklıdır:

```c
x = 5;
```

atamadır.

```c
x == 5
```

karşılaştırmadır.

### Mantıksal işleçler

```text
&&  VE
||  VEYA
!   DEĞİL
```

C'de `&&` ve `||` kısa devre değerlendirmesi yapar.

Örneğin:

```c
if (p != NULL && *p > 0) {
    ...
}
```

ifadesinde `p == NULL` ise `*p` değerlendirilmez.

### Bit işleçleri

```text
&   bit VE
|   bit VEYA
^   bit XOR
~   bit tümleme
<<  sola kaydırma
>>  sağa kaydırma
```

Mantıksal `&&` ile bit düzeyindeki `&` aynı değildir.

Bit işleçleri:

- bayraklar,
- protokoller,
- register erişimi,
- bit maskeleri,
- sıkıştırılmış veri

üzerinde önemlidir.

### if

```c
if (score >= 50) {
    passed = 1;
}
```

`else` ile:

```c
if (score >= 50) {
    passed = 1;
} else {
    passed = 0;
}
```

### İç içe if

```c
if (x > 0) {
    if (x % 2 == 0) {
        ...
    }
}
```

Derin iç içe koşullar okunabilirliği azaltabilir. Gerektiğinde koşullar sadeleştirilmeli veya işlem fonksiyonlara ayrılmalıdır.

### Koşul işleci

```c
condition ? value_if_true : value_if_false
```

Örnek:

```c
int max = a > b ? a : b;
```

Küçük değer seçimlerinde kullanışlıdır. Büyük iş bloklarını `?:` içine taşımak okunabilirliği azaltır.

### switch

```c
switch (command) {
case 1:
    start();
    break;
case 2:
    stop();
    break;
default:
    handle_unknown();
    break;
}
```

`break` kullanılmazsa yürütme sonraki `case` bölümüne düşebilir. Bilinçli fall-through açık biçimde belirtilmelidir.

### for

```c
for (int i = 0; i < 10; ++i) {
    printf("%d\n", i);
}
```

Üç bölüm:

```text
başlangıç
koşul
güncelleme
```

işlevini görür.

### while

```c
while (condition) {
    ...
}
```

Koşul ilk yinelemeden önce sınanır.

### do-while

```c
do {
    ...
} while (condition);
```

Gövde en az bir kez yürütülür.

### break

En yakın döngü veya `switch` yapısından çıkar:

```c
if (found) {
    break;
}
```

### continue

Mevcut yinelemenin kalanını atlar ve döngünün sonraki adımına geçer.

### goto

C dilinde `goto` vardır:

```c
goto cleanup;
```

Genel kontrol akışında gereksiz kullanımı okunabilirliği bozar. Bununla birlikte C'de birden fazla kaynak edinilmişse tek bir hata temizleme yoluna gitmek için kontrollü kullanım bazen pratiktir:

```c
FILE *f = fopen(path, "rb");
if (f == NULL) {
    return -1;
}

void *buffer = malloc(size);
if (buffer == NULL) {
    goto close_file;
}

/* işlem */

free(buffer);
fclose(f);
return 0;

close_file:
fclose(f);
return -1;
```

Bu tür kullanım rastgele dallanma değil, yapılandırılmış hata temizleme tekniğidir.

## Ünite 4: Diziler ve Karakter Dizileri

### Tek boyutlu diziler

```c
int values[5];
```

beş `int` elemanı için ardışık saklama alanı oluşturur.

İndisler:

```text
0, 1, 2, 3, 4
```

aralığındadır.

C sınır denetimi yapmaz. Dizinin dışına erişmek tanımsız davranıştır.

### Başlangıç değeri

```c
int values[5] = {10, 20, 30, 40, 50};
```

Boyut çıkarılabilir:

```c
int values[] = {10, 20, 30};
```

Kısmi başlangıç:

```c
int values[5] = {1, 2};
```

kalan elemanları sıfırlar.

### Eleman sayısı

Aynı kapsamda gerçek dizi için:

```c
size_t count = sizeof values / sizeof values[0];
```

kullanılabilir.

Ancak dizi fonksiyon parametresi olarak geldiğinde çoğunlukla işaretçiye dönüşür. Bu yöntem fonksiyon parametresi üzerinde dizi uzunluğunu vermez.

### Çok boyutlu diziler

```c
int matrix[3][4];
```

üç satır ve dört sütundan oluşan gerçek bir dizi yapısıdır.

C'de satır-major yerleşim kullanılır. Son indis bellekte ardışık değişir.

Eleman:

```c
matrix[i][j]
```

ile alınır.

### Matris toplama

Aynı boyutlu matrislerde:

```c
for (size_t i = 0; i < rows; ++i) {
    for (size_t j = 0; j < cols; ++j) {
        c[i][j] = a[i][j] + b[i][j];
    }
}
```

### Matris çarpımı

`A` matrisi `m x n`, `B` matrisi `n x p` ise sonuç `m x p` olur:

```text
C[i][j] = sum A[i][k] B[k][j]
```

Klasik algoritma:

```c
for (size_t i = 0; i < m; ++i) {
    for (size_t j = 0; j < p; ++j) {
        c[i][j] = 0;

        for (size_t k = 0; k < n; ++k) {
            c[i][j] += a[i][k] * b[k][j];
        }
    }
}
```

### C karakter dizileri

C'de ayrı bir yerleşik `string` türü yoktur. Klasik metin temsili `'\0'` ile sonlanan `char` dizisidir.

```c
char name[] = "Ali";
```

bellekte:

```text
'A' 'l' 'i' '\0'
```

olarak tutulur.

Burada `NULL` ile `'\0'` aynı kavram değildir.

- `'\0'`: değeri sıfır olan karakterdir.
- `NULL`: null pointer constant için kullanılan makrodur.

### Güvenli metin okuma

Tarihsel C kaynaklarında sık görülen:

```c
gets(buffer);
```

işlevi güvenli değildir ve C11 ile standarttan çıkarılmıştır. Alınacak verinin uzunluğunu sınırlayamadığı için kullanılmamalıdır.

Yerine:

```c
fgets(buffer, sizeof buffer, stdin);
```

kullanılabilir.

### Karakter dizisi uzunluğu

```c
#include <string.h>

size_t n = strlen(text);
```

`strlen`, sonlandırıcı `'\0'` karakterini saymaz.

Çalışma süresi metin uzunluğu kadar ilerleme gerektirdiği için:

```text
O(n)
```

düzeyindedir.

### Kopyalama

Standart:

```c
strcpy(destination, source);
```

işlevi hedef tamponun yeterli büyüklükte olduğunu varsayar. Kapasite denetlenmeden kullanımı taşmaya yol açabilir.

Boyutu açık olarak yönetmek çoğu durumda daha güvenlidir.

Örneğin:

```c
if (strlen(source) + 1 <= destination_size) {
    memcpy(destination, source, strlen(source) + 1);
}
```

Güvenli C kodunda tampon kapasitesi veriyle birlikte düşünülmelidir.

### Birleştirme

```c
strcat(destination, source);
```

hedefte yeterli boş alan olmasını gerektirir.

Metin birleştirme kodunda kapasite hesabı yapılmadan `strcat` kullanmak güvenli değildir.

### Karşılaştırma

```c
strcmp(a, b)
```

sonucu:

- negatif: `a < b`,
- sıfır: eşit,
- pozitif: `a > b`

anlamındadır.

Metinler:

```c
a == b
```

ile içerik bakımından karşılaştırılmaz. Bu ifade işaretçi veya dizi bağlamına göre adres davranışıyla ilgilidir.

## Ünite 5: İşaretçiler ve Bellek

### İşaretçi kavramı

İşaretçi, başka bir nesnenin veya fonksiyonun adresini temsil edebilen türdür.

```c
int value = 10;
int *p = &value;
```

Burada:

- `value`: `int`,
- `&value`: `int *`,
- `p`: `int *`,
- `*p`: `int`

türündedir.

### Adres alma

```c
&value
```

nesnenin adresini verir.

### Dolaylı erişim

```c
*p
```

işaret edilen nesneye erişir.

```c
*p = 20;
```

sonrasında:

```text
value == 20
```

olur.

### Null işaretçi

Bir işaretçinin geçerli bir nesneyi göstermediğini belirtmek için:

```c
int *p = NULL;
```

kullanılabilir.

Null işaretçiyi dereference etmek:

```c
*p
```

tanımsız davranıştır.

### İşaretçi aritmetiği

Dizi elemanını gösteren işaretçide:

```c
p + 1
```

adres değerini bir bayt değil, işaret edilen türün bir elemanı kadar ilerletir.

Örnek:

```c
int a[4] = {10, 20, 30, 40};
int *p = a;

printf("%d\n", *(p + 2));
```

çıktı:

```text
30
```

olur.

İşaretçi aritmetiği yalnız aynı dizi nesnesi ve bir-past-the-end sınırları içinde tanımlı davranışa sahiptir.

### Dizi ve işaretçi ilişkisi

Birçok ifadede:

```c
array
```

ilk elemanın işaretçisine dönüşür:

```c
&array[0]
```

Ancak dizi ve işaretçi aynı tür değildir.

Örneğin:

```c
sizeof array
```

dizinin tamamının boyutunu verebilirken:

```c
sizeof pointer
```

yalnız işaretçinin boyutunu verir.

### İşaretçi ile dizi dolaşma

```c
for (int *p = a; p != a + count; ++p) {
    printf("%d\n", *p);
}
```

indisli sürümle aynı temel belleği dolaşır:

```c
for (size_t i = 0; i < count; ++i) {
    printf("%d\n", a[i]);
}
```

### İşaretçi dizisi

```c
const char *names[] = {
    "Ali",
    "Ayse",
    "Mehmet"
};
```

Burada `names` karakter dizilerinin kendisini değil, karakter dizilerine işaret eden işaretçileri tutar.

### İşaretçiye işaret eden işaretçi

```c
int value = 5;
int *p = &value;
int **pp = &p;
```

`**pp` ile `value` değerine ulaşılabilir.

Bu yapı:

- dinamik iki boyutlu yapılar,
- çıktı parametreleri,
- `argv`,
- işaretçi değişkeninin değiştirilmesi

gibi durumlarda görülebilir.

### Dinamik bellek ayırma

C'de:

```c
#include <stdlib.h>

int *p = malloc(count * sizeof *p);
```

ile dinamik alan ayrılabilir.

Başarı denetlenmelidir:

```c
if (p == NULL) {
    /* hata */
}
```

Kullanım bitince:

```c
free(p);
p = NULL;
```

yapılabilir.

### calloc

```c
int *p = calloc(count, sizeof *p);
```

ayrılan bütün baytları sıfır bit değerleriyle başlatır.

Bu durum her tür için semantik olarak "sıfır değerli nesne dizisi" anlamına otomatik olarak indirgenmemelidir. Tamsayı türlerinde pratik ve standart uyumlu kullanım yaygındır.

### realloc

```c
int *temp = realloc(p, new_count * sizeof *p);
```

başarısız olduğunda eski `p` hâlâ geçerli olabileceği için sonucu doğrudan `p` üzerine yazmak risklidir.

Güvenli desen:

```c
int *temp = realloc(p, new_count * sizeof *p);

if (temp != NULL) {
    p = temp;
}
```

### Bellek hataları

C'de önemli hata sınıfları:

- null dereference,
- buffer overflow,
- out-of-bounds erişim,
- use-after-free,
- double free,
- dangling pointer,
- memory leak,
- uninitialized read.

Bu hataların çoğu derleme sırasında yakalanmayabilir.

Derleyici uyarıları ve çalışma zamanı araçları önemlidir:

```text
-Wall
-Wextra
-Wpedantic
-fsanitize=address
-fsanitize=undefined
```

gibi seçenekler geliştirme sırasında yararlı olabilir.

## Ünite 6: C Fonksiyonları

### Fonksiyon bildirimi

Prototip:

```c
int sum(int a, int b);
```

Tanım:

```c
int sum(int a, int b)
{
    return a + b;
}
```

Çağrı:

```c
int result = sum(3, 4);
```

### void fonksiyon

Değer döndürmeyen fonksiyon:

```c
void print_message(void)
{
    puts("Merhaba");
}
```

`void` parametre listesi C'de fonksiyonun parametre almadığını açıkça belirtir.

### Parametre aktarımı

C'de parametreler **değer ile** aktarılır.

```c
void f(int x)
{
    x = 10;
}
```

çağıranın değişkenini değiştirmez.

Kaynaklarda bazen "referans ile çağırma" denilen C tekniği gerçekte işaretçi değerinin fonksiyona geçirilmesidir.

```c
void set_value(int *p)
{
    *p = 10;
}
```

çağrı:

```c
int x = 0;
set_value(&x);
```

Burada yine `p` parametresinin kendisi değer ile aktarılmıştır. Ancak bu değer `x` nesnesinin adresidir.

C'de C++ anlamındaki reference türü yoktur.

### Çıktı parametresi

Bir fonksiyon birden çok sonuç üretecekse:

```c
int divide(int a, int b, int *quotient, int *remainder)
{
    if (b == 0) {
        return 0;
    }

    *quotient = a / b;
    *remainder = a % b;
    return 1;
}
```

kullanılabilir.

### Diziyi fonksiyona aktarma

```c
double average(const int values[], size_t count);
```

parametre bağlamında:

```c
const int values[]
```

işlevsel olarak işaretçi parametresidir.

Bu nedenle eleman sayısı ayrıca taşınmalıdır.

### const parametre

Fonksiyon işaret edilen veriyi değiştirmeyecekse:

```c
void print_array(const int *values, size_t count);
```

bildirimi niyeti açıklar ve yanlış değişikliği engeller.

### Küresel değişkenlerle veri paylaşımı

Küresel değişken fonksiyonlar arasında veri paylaşabilir, ancak:

- bağımlılıkları gizler,
- testleri zorlaştırır,
- eşzamanlılık sorunları oluşturabilir.

Fonksiyon parametreleri ve açık veri yapıları çoğu zaman daha iyi tasarım sağlar.

### static yerel değişken

```c
int next_id(void)
{
    static int id = 0;
    return ++id;
}
```

`id` yalnız fonksiyon kapsamında görünür fakat program boyunca varlığını sürdürür.

Bu tür gizli durum thread-safety ve yeniden giriş açısından ayrıca değerlendirilmelidir.

### Özyineleme

Fonksiyon kendisini çağırabilir.

Faktöriyel:

```c
unsigned long long factorial(unsigned n)
{
    if (n <= 1) {
        return 1;
    }

    return n * factorial(n - 1);
}
```

Her özyinelemeli algoritmada:

- taban durum,
- taban duruma ilerleme

bulunmalıdır.

Özyineleme çağrı yığını kullanır. Çok derin özyineleme stack overflow oluşturabilir.

## Ünite 7: C Yapıları, Birlikler ve Bit Alanları

### struct

`struct`, farklı türde alanları tek veri yapısında birleştirir.

```c
struct Student {
    int id;
    char name[64];
    double grade;
};
```

Nesne:

```c
struct Student s;
```

Alan erişimi:

```c
s.id = 10;
```

### typedef

```c
typedef struct {
    int x;
    int y;
} Point;
```

sonrasında:

```c
Point p;
```

kullanılabilir.

C23 ile etiket ve typedef ihtiyaçları bazı yeni dil olanaklarıyla sadeleşmiş olsa da geleneksel biçim çok yaygındır.

### Başlangıç değeri

```c
struct Point {
    int x;
    int y;
};

struct Point p = {10, 20};
```

Belirlenmiş başlatıcı:

```c
struct Point p = {
    .x = 10,
    .y = 20
};
```

özellikle çok alanlı yapılarda okunabilirliği artırır.

### Yapı işaretçisi

```c
struct Student *p = &s;
```

alan:

```c
p->id
```

ile alınır.

Bu:

```c
(*p).id
```

ifadesinin kısa biçimidir.

### Yapının fonksiyona aktarılması

Değer ile:

```c
void print_point(struct Point p);
```

veya işaretçi ile:

```c
void print_point(const struct Point *p);
```

aktarılabilir.

Büyük yapılarda gereksiz kopyayı önlemek ve salt okunur erişimi belirtmek için `const` işaretçi uygun olabilir.

### Dizi içinde struct

```c
struct Student students[100];
```

yapısında her eleman bir `Student` kaydıdır.

### union

Birlikte bütün üyeler aynı depolama alanını paylaşır:

```c
union Value {
    int i;
    double d;
};
```

Aynı anda alanın yalnız uygun aktif yorumunun kullanılması gerekir.

`union`:

- protokol çözümleme,
- donanım temsili,
- tagged union

gibi yapılarda kullanılabilir, ancak tür yorumlama kuralları dikkatle ele alınmalıdır.

### Etiketli birlik

Birliğin hangi üyesinin geçerli olduğunu ayrıca tutmak daha güvenlidir:

```c
enum ValueType {
    VALUE_INT,
    VALUE_DOUBLE
};

struct Value {
    enum ValueType type;
    union {
        int i;
        double d;
    } data;
};
```

### Bit alanları

```c
struct Flags {
    unsigned ready : 1;
    unsigned mode  : 3;
};
```

belirli bit genişlikli alanlar tanımlar.

Ancak:

- alan yerleşimi,
- sıralama,
- padding

gibi ayrıntılar gerçekleştirim tanımlı olabilir.

Ağ protokolü veya kalıcı dosya biçimi gibi tam bit düzeninin zorunlu olduğu yapılarda açık maskeler ve kaydırmalar daha taşınabilir olabilir.

## Ünite 8: C Dosya İşlemleri ve Standart Kütüphane

### Dosya açma

```c
FILE *f = fopen("data.txt", "r");
```

Başarı denetlenmelidir:

```c
if (f == NULL) {
    perror("fopen");
    return 1;
}
```

### Açma kipleri

Yaygın kipler:

```text
r   oku
w   yaz, varsa içeriği sıfırla
a   sona ekle
r+  oku/yaz
w+  oku/yaz, içeriği sıfırla
a+  oku ve sona ekle
```

İkili kip için:

```text
rb
wb
ab
```

gibi biçimler kullanılır.

Özellikle Windows gibi metin ve ikili kip ayrımı bulunan sistemlerde doğru kip önemlidir.

### Dosya kapatma

```c
if (fclose(f) != 0) {
    /* hata */
}
```

Yazma hatalarının bir bölümü tampon boşaltılırken ortaya çıkabileceği için `fclose` sonucu önemli olabilir.

### Karakter okuma ve yazma

```c
int ch = fgetc(f);
```

`fgetc` dönüş türü `int`tir; çünkü bütün `unsigned char` değerlerinin yanında `EOF` değerini de temsil etmesi gerekir.

Yazma:

```c
fputc('A', f);
```

### Satır okuma

```c
char line[256];

while (fgets(line, sizeof line, f) != NULL) {
    ...
}
```

tampon sınırı belirli olduğundan `gets` kullanımından daha güvenlidir.

### Biçimlendirilmiş G/Ç

Yazma:

```c
fprintf(f, "%d %.2f\n", id, value);
```

Okuma:

```c
int n = fscanf(f, "%d %lf", &id, &value);
```

`scanf` ailesinde dönüş değeri okunabilen alan sayısını gösterir ve mutlaka denetlenmelidir.

### İkili yazma

```c
size_t written = fwrite(buffer, element_size, count, f);
```

İkili okuma:

```c
size_t read_count = fread(buffer, element_size, count, f);
```

Sonuçlar beklenen eleman sayısıyla karşılaştırılmalıdır.

### Struct'ı doğrudan dosyaya yazmak

```c
fwrite(&record, sizeof record, 1, f);
```

aynı program içinde geçici veri için çalışabilir, ancak taşınabilir kalıcı dosya biçimi değildir.

Nedenleri:

- padding,
- endian,
- tür genişliği,
- ABI,
- derleyici,
- sürüm değişiklikleri.

Kalıcı veya ağ üzerinden taşınan veri için açık serileştirme biçimi kullanılmalıdır.

### Dosya konumu

```c
fseek(f, offset, SEEK_SET);
long pos = ftell(f);
rewind(f);
```

rastgele erişim için kullanılabilir.

Büyük dosyalarda platformun büyük dosya API'leri ve `off_t` benzeri türleri gerekebilir.

### Dosya silme ve yeniden adlandırma

```c
remove("file.txt");
rename("old.txt", "new.txt");
```

standart C kütüphanesinde bulunur.

Dosya sistemi izinleri ve yarış koşulları işletim sistemine bağlıdır.

### Karakter sınıflandırma

```c
#include <ctype.h>

isdigit(c)
isalpha(c)
isspace(c)
toupper(c)
tolower(c)
```

Bu işlevlere `EOF` veya `unsigned char` olarak temsil edilebilen değerler verilmelidir. Negatif `char` değerini doğrudan geçirmek tanımsız davranış oluşturabilir.

Güvenli kalıp:

```c
toupper((unsigned char)c)
```

### Matematik kütüphanesi

```c
#include <math.h>

sqrt
pow
sin
cos
tan
log
log10
exp
fabs
```

işlevleri yaygındır.

### Tarih ve zaman

```c
#include <time.h>
```

başlığı:

- `time`,
- `difftime`,
- `mktime`,
- `strftime`

gibi olanaklar sağlar.

Yerel saat, zaman dilimi ve takvim işlemleri platform ayrıntıları içerdiğinden salt saniye değerinden daha karmaşıktır.

## Ünite 9: C Önişlemcisi

### Önişleme

`#` ile başlayan yönergeler derlemenin sözdizim çözümünden önce önişlemci tarafından işlenir.

Başlıca kullanım:

- dosya dahil etme,
- makro,
- koşullu derleme,
- derleme zamanı yapılandırması.

### Nesne benzeri makro

```c
#define BUFFER_SIZE 4096
```

Makro metinsel yer değiştirmedir. Türü yoktur.

Bir sabit nesne veya `enum` daha uygun olabiliyorsa makro kullanmak zorunlu değildir.

### Fonksiyon benzeri makro

```c
#define SQUARE(x) ((x) * (x))
```

Parantez kullanımı öncelik sorunlarını azaltır.

Buna rağmen:

```c
SQUARE(i++)
```

tehlikelidir, çünkü `i++` birden fazla değerlendirilir.

C'de uygun olduğunda `inline` fonksiyon daha güvenlidir:

```c
static inline int square_int(int x)
{
    return x * x;
}
```

### # ve ##

Stringification:

```c
#define TO_STRING(x) #x
```

Token birleştirme:

```c
#define JOIN(a, b) a##b
```

İleri makro metaprogramlamada kullanılır ancak gereksiz karmaşıklık oluşturabileceğinden ölçülü kullanılmalıdır.

### #undef

```c
#undef NAME
```

daha önce tanımlanmış makroyu kaldırır.

### Koşullu derleme

```c
#ifdef DEBUG
    ...
#endif
```

ve:

```c
#if FEATURE_LEVEL >= 2
    ...
#elif FEATURE_LEVEL == 1
    ...
#else
    ...
#endif
```

kullanılabilir.

### Header guard

```c
#ifndef MY_HEADER_H
#define MY_HEADER_H

/* bildirimler */

#endif
```

aynı başlığın bir çeviri biriminde birden fazla kez etkili olmasını önler.

`#pragma once` birçok derleyicide desteklenir, ancak ISO C standardının zorunlu bir yönergesi değildir.

## Bu Çalışmaya Atıf

Köker, M. A. (2014). C Programlama Temelleri. alikoker.com.tr. https://alikoker.com.tr/c-programlama-temelleri

- BibTeX: https://alikoker.com.tr/c-programlama-temelleri.bib
- RIS: https://alikoker.com.tr/c-programlama-temelleri.ris
- CSL-JSON: https://alikoker.com.tr/c-programlama-temelleri.csl.json
