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:
#include <iostream>satırı silinir, yerineiostreamdosyasının tüm içeriği yapıştırılır (binlerce satır).#define PI 3.14159satırı silinir, koddaki tümPIkelimeleri3.14159ile değiştirilir.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:
#includebir "akıllı import" değildir. Gerçekten dosyanın tüm içeriğini kopyala-yapıştır yapar. Bu yüzden gereksiz#includesatı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
.odosyası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_adiBu komut tüm 4 aşamayı tek seferde yapar ve çalıştırılabilir bir dosya üretir.
Sık Kullanılan Flag'ler
| Flag | Açıklama | Örnek |
|---|---|---|
-o <isim> | Çıktı dosyasının adını belirler | g++ main.cpp -o uygulama |
-Wall | Tüm uyarıları (warning) gösterir | g++ -Wall main.cpp -o uygulama |
-Wextra | Ekstra uyarılar | -Wall -Wextra birlikte kullanılır |
-Werror | Uyarıları hata olarak değerlendirir | Uyarıları görmezden gelemezsin |
-std=c++17 | C++17 standardını kullan | g++ -std=c++17 main.cpp -o uygulama |
-std=c++20 | C++20 standardını kullan | C++20 özellikleri için |
-g | Debug bilgisi ekler | Hata ayıklama (debugging) için |
-O2 | Optimizasyon seviyesi 2 | Performans için |
-c | Sadece derleme (linking yok) | Object dosyası üretir |
-E | Sadece preprocessing | Preprocessor çıktısı |
-S | Assembly kodu üret | Assembly çıktısı |
Önerilen Geliştirme Komutu
g++ -std=c++17 -Wall -Wextra -Werror -g main.cpp -o programBu 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 programYa da tek satırda:
g++ main.cpp matematik.cpp -o programHeader 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);
#endifKaynak 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 sonuModern 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 oncekoymayı alışkanlık edin.
Object Dosyası vs Executable Farkı
Bu iki kavramı karıştırmak kolay. Netleştirelim:
| Özellik | Object Dosyası (.o) | Executable |
|---|---|---|
| İçerik | Tek bir kaynak dosyanın makine kodu | Tüm kodun birleştirilmiş hali |
| Çalıştırılabilir mi? | Hayır | Evet |
| Dış bağımlılıklar | Çözümlenmemiş (unresolved) | Tüm bağımlılıklar çözülmüş |
| Oluşturan | Compiler + Assembler | Linker |
| Komut | g++ -c dosya.cpp | g++ 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.exeAdı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:
filekomutu 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,
#includeve#definegibi 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;
#includebunları 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.
AI Asistan
Sorularını yanıtlamaya hazır