← Kursa Dön
📄 Text · 15 min

Derleme Süreci: Kaynak Koddan Çalışan Programa

Bir C++ dosyası yazdın diyelim. .cpp uzantılı bir metin dosyası. Ama bilgisayarın bu dosyayı doğrudan çalıştıramaz — bilgisayar sadece 0 ve 1'lerden oluşan makine kodunu anlar. Peki bu metin dosyası nasıl çalışan bir programa dönüşür?

İşte bu sürecin adı derleme (compilation). Bu derste kaynak kodunun 4 aşamada nasıl çalıştırılabilir bir programa dönüştüğünü öğreneceğiz.


Büyük Resim: 4 Aşama

C++ kodunun çalışan bir programa dönüşmesi 4 aşamadan geçer:

Kaynak Kod (.cpp)
       ↓
  1. Preprocessing (Ön İşleme)
       ↓
  2. Compilation (Derleme)
       ↓
  3. Assembly (Montaj)
       ↓
  4. Linking (Bağlama)
       ↓
Çalıştırılabilir Dosya (executable)

🏭 Analoji: Bu süreci bir fabrika gibi düşün. Hammadde (kaynak kod) önce temizlenir ve hazırlanır (preprocessing), sonra parçalar üretilir (compilation), parçalar standart formata dönüştürülür (assembly) ve son olarak tüm parçalar birleştirilerek nihai ürün (executable) ortaya çıkar.

Şimdi her aşamayı teker teker inceleyelim.


Aşama 1: Preprocessing (Ön İşleme)

Preprocessor, derleme başlamadan önce kaynak kodunu hazırlayan bir metin işlemcisidir. # ile başlayan tüm satırlar (directive) preprocessor'ın işidir.

Ne yapar?

  • `#include` işleme: Header dosyalarının içeriğini doğrudan kodun içine yapıştırır.

  • `#define` değiştirme: Makroları gerçek değerleriyle değiştirir.

  • `#ifdef/#endif` koşullu derleme: Koşula göre bazı kod bloklarını dahil eder veya çıkarır.

  • Yorum silme: Tüm yorumları temizler.

Somut Örnek

Diyelim şu kodu yazdın:

// main.cpp
#include <iostream>
#define PI 3.14159

int main() {
    // Dairenin alanini hesapla
    double yaricap = 5.0;
    double alan = PI * yaricap * yaricap;
    std::cout << "Alan: " << alan << std::endl;
    return 0;
}

Preprocessor bu kodu şöyle dönüştürür:

  1. #include <iostream> satırı silinir, yerine iostream dosyasının tüm içeriği yapıştırılır (binlerce satır).

  2. #define PI 3.14159 satırı silinir, koddaki tüm PI kelimeleri 3.14159 ile değiştirilir.

  3. Yorum satırları temizlenir.

Preprocessor çıktısını görmek

g++ -E main.cpp -o main.i

-E flag'i derleyiciye "sadece preprocessing yap, dur" der. Çıktı dosyasını (main.i) açarsan binlerce satır göreceksin — bunların çoğu iostream header'ından gelen tanımlamalar.

⚠️ Dikkat: #include bir "akıllı import" değildir. Gerçekten dosyanın tüm içeriğini kopyala-yapıştır yapar. Bu yüzden gereksiz #include satırları derleme süresini uzatır.


Aşama 2: Compilation (Derleme)

Preprocessing bittikten sonra derleyici asıl işine başlar: C++ kodunu assembly diline çevirir.

Ne yapar?

  • Söz dizimi kontrolü: Kodun C++ kurallarına uygun mu kontrol eder.

  • Tip kontrolü: Değişken tipleri uyumlu mu denetler.

  • Optimizasyon: Kodu daha verimli hale getirir (opsiyonel).

  • Assembly kodu üretimi: Platforma özel assembly talimatları oluşturur.

Assembly çıktısını görmek

g++ -S main.cpp -o main.s

-S flag'i derleyiciye "assembly kodunu üret, dur" der. Çıktı şöyle bir şey olur:

    .file   "main.cpp"
    .text
    .globl  main
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movsd   .LC0(%rip), %xmm0    ; yaricap = 5.0
    ...

Bunu anlamak zorunda değilsin. Sadece derleyicinin C++ kodunu bilgisayarın anlayacağı daha düşük seviyeli talimatlara çevirdiğini bil.

Derleyici Hataları Burada Çıkar

Eğer kodunda bir söz dizimi hatası varsa, derleyici bu aşamada seni uyarır:

int main() {
    std::cout << "Merhaba"    // noktalı virgül yok!
    return 0;
}
main.cpp:3:36: error: expected ';' before 'return'

Hata mesajlarını okumayı öğrenmek, C++ yolculuğunun en önemli becerilerinden biri.


Aşama 3: Assembly (Montaj)

Assembler, assembly kodunu object dosyasına (.o veya .obj) çevirir. Object dosyası makine kodunu içerir ama henüz çalıştırılabilir değildir.

Ne yapar?

  • Assembly talimatlarını makine koduna (binary) çevirir.

  • Object dosyası (.o) üretir.

Object dosyası oluşturmak

g++ -c main.cpp -o main.o

-c flag'i derleyiciye "object dosyası üret, linking yapma" der.

Object dosyası neden çalışmaz?

Object dosyası kendi kodunun makine karşılığını içerir ama dışarıdan kullandığı fonksiyonların adreslerini bilmez. Mesela std::cout kullandın ama cout'un gerçek kodu başka bir yerde (standart kütüphanede). Bu adreslerin çözülmesi bir sonraki aşamada olur.


Aşama 4: Linking (Bağlama)

Linker, tüm parçaları birleştiren son aşamadır. Birden fazla object dosyasını ve kütüphaneleri alıp tek bir çalıştırılabilir dosya (executable) üretir.

Ne yapar?

  • Sembol çözümleme (symbol resolution): Her fonksiyon çağrısının gerçek adresini bulur.

  • Object dosyalarını birleştirir: Birden fazla .o dosyasını tek bir programa bağlar.

  • Kütüphaneleri bağlar: Standart kütüphane (libstdc++) ve diğer kütüphaneleri ekler.

  • Çalıştırılabilir dosya üretir: Sonuç: çalışan bir program!

Linker hataları

Eğer kullandığın bir fonksiyonun tanımı bulunamazsa, linker hatası alırsın:

// main.cpp
void selamVer();  // Bildirim var ama tanım yok!

int main() {
    selamVer();
    return 0;
}
undefined reference to 'selamVer()'

Bu compiler hatası değil, linker hatasıdır. Derleyici selamVer() fonksiyonunun bildirimi olduğu için kodu derledi ama linker tanımını bulamadı.

💡 İpucu: Compiler hataları genellikle satır numarası verir. Linker hataları ise fonksiyon ismi verir. Bu ayrımı bilmek hata ayıklamada çok işe yarar.


g++ Komutu ve Temel Flag'ler

g++ GNU C++ derleyicisidir ve Linux dünyasında en yaygın kullanılan derleyicidir. Temel kullanımı:

g++ kaynak.cpp -o program_adi

Bu komut tüm 4 aşamayı tek seferde yapar ve çalıştırılabilir bir dosya üretir.

Sık Kullanılan Flag'ler

FlagAçıklamaÖrnek
-o <isim>Çıktı dosyasının adını belirlerg++ main.cpp -o uygulama
-WallTüm uyarıları (warning) gösterirg++ -Wall main.cpp -o uygulama
-WextraEkstra uyarılar-Wall -Wextra birlikte kullanılır
-WerrorUyarıları hata olarak değerlendirirUyarıları görmezden gelemezsin
-std=c++17C++17 standardını kullang++ -std=c++17 main.cpp -o uygulama
-std=c++20C++20 standardını kullanC++20 özellikleri için
-gDebug bilgisi eklerHata ayıklama (debugging) için
-O2Optimizasyon seviyesi 2Performans için
-cSadece derleme (linking yok)Object dosyası üretir
-ESadece preprocessingPreprocessor çıktısı
-SAssembly kodu üretAssembly çıktısı

Önerilen Geliştirme Komutu

g++ -std=c++17 -Wall -Wextra -Werror -g main.cpp -o program

Bu komut:

  • C++17 standardını kullanır

  • Tüm uyarıları gösterir

  • Uyarıları hata sayar (temiz kod yazmaya zorlar)

  • Debug bilgisi ekler

Birden Fazla Dosya Derleme

Büyük projelerde kod birden fazla dosyaya bölünür:

# Her dosyayı ayrı ayrı derle
g++ -c main.cpp -o main.o
g++ -c matematik.cpp -o matematik.o

# Object dosyalarını birleştir (linking)
g++ main.o matematik.o -o program

Ya da tek satırda:

g++ main.cpp matematik.cpp -o program

Header Dosyaları ve #include Mantığı

C++'ta kodun organizasyonu için iki tür dosya kullanılır:

Header Dosyaları (.h veya .hpp)

Bildirimleri (declaration) içerir: fonksiyon prototipleri, sınıf tanımları, sabitler.

// matematik.h
#ifndef MATEMATIK_H
#define MATEMATIK_H

double topla(double a, double b);
double cikar(double a, double b);

#endif

Kaynak Dosyaları (.cpp)

Tanımları (definition) içerir: fonksiyonların gerçek kodu.

// matematik.cpp
#include "matematik.h"

double topla(double a, double b) {
    return a + b;
}

double cikar(double a, double b) {
    return a - b;
}

Ana Dosya

// main.cpp
#include <iostream>
#include "matematik.h"

int main() {
    double sonuc = topla(3.5, 2.1);
    std::cout << "Sonuc: " << sonuc << std::endl;
    return 0;
}

< > vs " " Farkı

  • `#include <iostream>` — Sistem/standart kütüphane header'ları. Derleyicinin bildiği dizinlerde arar.

  • `#include "matematik.h"` — Kendi yazdığın header'lar. Önce mevcut dizinde, bulamazsa sistem dizinlerinde arar.

Include Guard Nedir?

Header dosyalarında gördüğün #ifndef / #define / #endif kalıbına include guard denir. Aynı header dosyasının birden fazla kez dahil edilmesini engeller.

#ifndef MATEMATIK_H    // Eğer MATEMATIK_H tanımlı değilse
#define MATEMATIK_H    // MATEMATIK_H'ı tanımla

// ... header içeriği ...

#endif                 // Blok sonu

Modern alternatif:

#pragma once    // Aynı işi tek satırda yapar

double topla(double a, double b);
double cikar(double a, double b);

#pragma once daha kısa ve okunaklı ama resmi standartta değil. Pratikte tüm büyük derleyiciler destekliyor.

⚠️ Dikkat: Include guard kullanmazsan aynı header iki kez dahil edildiğinde "redefinition" (tekrar tanımlama) hatası alırsın. Her header dosyasına include guard veya #pragma once koymayı alışkanlık edin.


Object Dosyası vs Executable Farkı

Bu iki kavramı karıştırmak kolay. Netleştirelim:

ÖzellikObject Dosyası (.o)Executable
İçerikTek bir kaynak dosyanın makine koduTüm kodun birleştirilmiş hali
Çalıştırılabilir mi?HayırEvet
Dış bağımlılıklarÇözümlenmemiş (unresolved)Tüm bağımlılıklar çözülmüş
OluşturanCompiler + AssemblerLinker
Komutg++ -c dosya.cppg++ dosya.o -o program

Neden ayrı ayrı derlenir?

Büyük projelerde sadece değişen dosyayı yeniden derlemek isteyebilirsin. Eğer matematik.cpp'yi değiştirdin ama main.cpp'ye dokunmadıysan, sadece matematik.cpp'yi yeniden derleyip linking yapmak yeterli. Bu, derleme süresini dramatik şekilde azaltır.

Bu mantığı otomatikleştiren araçlara build system denir: make, CMake, ninja gibi. İlerleyen derslerde bunlara değineceğiz.


Pratikte Derleme: Adım Adım

Şimdi öğrendiğimiz her şeyi bir araya getirelim.

Adım 1: Kaynak kodu yaz

// selam.cpp
#include <iostream>
#include <string>

int main() {
    std::string isim;
    std::cout << "Adinizi girin: ";
    std::getline(std::cin, isim);
    std::cout << "Merhaba, " << isim << "!" << std::endl;
    return 0;
}

Adım 2: Derle ve çalıştır

# Derleme
g++ -std=c++17 -Wall selam.cpp -o selam

# Çalıştırma (Linux/Mac)
./selam

# Çalıştırma (Windows)
selam.exe

Adım 3: Aşamaları tek tek gör

# 1. Preprocessing
g++ -E selam.cpp -o selam.i
wc -l selam.i   # Binlerce satır olacak!

# 2. Compilation (Assembly çıktısı)
g++ -S selam.cpp -o selam.s
cat selam.s      # Assembly kodu

# 3. Assembly (Object dosyası)
g++ -c selam.cpp -o selam.o
file selam.o     # "ELF relocatable" yazacak

# 4. Linking (Executable)
g++ selam.o -o selam
file selam       # "ELF executable" yazacak

💡 İpucu: file komutu bir dosyanın tipini söyler. Object dosyası "relocatable" (henüz yerleştirilmemiş), executable ise "executable" (çalıştırılabilir) olarak gösterilir.


Yaygın Derleme Hataları

Yeni başlayanların en çok karşılaştığı hatalar:

1. Söz Dizimi Hatası (Syntax Error)

error: expected ';' before 'return'

Çözüm: Satır sonuna noktalı virgül koymayı unutmuşsun.

2. Tanımlanmamış Değişken

error: 'sayi' was not declared in this scope

Çözüm: Değişkeni kullanmadan önce tanımla veya yazım hatasını kontrol et.

3. Header Bulunamadı

fatal error: 'matematik.h' file not found

Çözüm: Dosya adını ve yolunu kontrol et. < > yerine " " kullanman gerekebilir.

4. Linker Hatası

undefined reference to 'fonksiyon()'

Çözüm: Fonksiyonu tanımladığın .cpp dosyasını da derlemeye dahil et.

5. Çoklu Tanımlama

multiple definition of 'fonksiyon()'

Çözüm: Fonksiyon tanımını header yerine .cpp dosyasına koy. Header'da sadece bildirim olmalı.


Özet

  • C++ kodu 4 aşamadan geçerek çalışan programa dönüşür: Preprocessing → Compilation → Assembly → Linking.

  • Preprocessor, #include ve #define gibi direktifleri işler; derleme başlamadan önce kodu hazırlar.

  • g++ ile -Wall (uyarılar), -std=c++17 (standart) ve -o (çıktı adı) flag'lerini her zaman kullan.

  • Header dosyaları (.h/.hpp) bildirimleri, kaynak dosyaları (.cpp) tanımları içerir; #include bunları birbirine bağlar.

  • Object dosyası tek bir kaynak dosyanın derlenmiş hali; executable ise tüm parçaların linker tarafından birleştirilmiş hali.

  • Compiler hataları satır numarası, linker hataları fonksiyon ismi verir — bu ayrımı bilmek hata ayıklamayı kolaylaştırır.