C++ “undefined reference to” linker hatası, kaynak kodu derlendikten sonra bağlama aşamasında ortaya çıkar. Çözüm için çağrılan fonksiyonun bildiriminin bulunduğunu, tanımının gerçekten yazıldığını ve tanımı içeren kaynak dosyasının ya da kütüphanenin g++ komutuna eklendiğini kontrol etmelisin.
Derleyici her kaynak dosyasını önce nesne dosyasına dönüştürür. Bağlayıcı ise bu nesne dosyalarını, fonksiyon tanımlarını ve gerekiyorsa kütüphaneleri birleştirerek çalıştırılabilir dosyayı oluşturur. GCC'nin bu aşamaları nasıl yürüttüğü resmî belgelerinde açıklanır.
undefined reference to hatası hangi aşamada oluşur?
undefined reference to hatası genellikle derleme sonrasında, bağlama aşamasında oluşur. Bildirim, bir fonksiyonun veya sınıf üyesinin adını ve imzasını derleyiciye tanıtan açıklamadır. Tanım ise bu işlevin gövdesini sağlayan koddur.
Derleyici bir çağrıyı gördüğünde bildirime bakarak çağrının sözdizimsel olarak geçerli olup olmadığını kontrol eder. Bağlayıcı daha sonra bu çağrının karşılık geldiği tanımı arar. Tanım bulunamazsa hata mesajında çağrılan sembolün adıyla birlikte undefined reference to ifadesi görünür.
int topla(int, int); // bildirim
int sonuc = topla(2, 3); // çağrı
int topla(int a, int b) { // tanım
return a + b;
}
Bu üç parça arasındaki ilişkiyi şöyle düşünebilirsin: bildirim, işlevin nasıl çağrılacağını söyler; çağrı bu işlevi kullanır; tanım ise yapılacak işlemi içerir. Bildirim ve çağrı bulunduğu hâlde tanım derleme sürecine katılmazsa, kaynak dosyası kendi içinde doğru görünebilir fakat çalıştırılabilir dosya oluşturulamaz.
Üç dosyalı örnekte eksik kaynak dosyası nasıl bulunur?

main.cpp, hesap.h ve hesap.cpp dosyalarından oluşan küçük bir projede bildirimi başlık dosyasına, tanımı kaynak dosyasına, çağrıyı ise main.cpp dosyasına yerleştirebilirsin.
hesap.h
#ifndef HESAP_H
#define HESAP_H
int topla(int a, int b);
#endif
hesap.cpp
#include "hesap.h"
int topla(int a, int b) {
return a + b;
}
main.cpp
#include <iostream>
#include "hesap.h"
int main() {
std::cout << topla(2, 3) << 'n';
return 0;
}
Bu projede yalnızca main.cpp dosyasını derleyip bağlamaya çalışırsan şu komut eksik kalır:
g++ main.cpp -o hesap
main.cpp içindeki bildirim derleyicinin çağrıyı tanımasına yeterlidir; ancak topla fonksiyonunun gövdesi hesap.cpp dosyasındadır. Bu dosya komuta eklenmediği için bağlayıcı gerekli tanımı bulamaz.
İki kaynak dosyasını birlikte derleyip bağlayan komut şöyledir:
g++ main.cpp hesap.cpp -o hesap
Bu komutun ardından oluşan program, 2 ile 3 değerlerini toplar ve ekrana 5 yazdırır. Hata mesajında topla gibi eksik sembol görünüyorsa, bu sembolün tanımını içeren hesap.cpp dosyasının derleme komutunda yer alıp almadığını kontrol etmelisin.
Sınıf üyesi tanımında Hesap:: neden gereklidir?
Bildirim, derleyiciye bir fonksiyonun varlığını ve imzasını haber verir. Tanım ise fonksiyonun gövdesini içerir. Bir sınıf üyesi dışarıda tanımlanırken, bu tanımın hangi sınıfa ait olduğunu Hesap:: kapsam belirteciyle belirtmek gerekir.
// hesap.h
class Hesap {
public:
void hesapla();
};
Yukarıdaki bildirimde hesapla fonksiyonunun Hesap sınıfına ait olduğu belirtilir. hesap.cpp içinde kapsam belirteci unutulursa aşağıdaki kod sınıf üyesini tanımlamaz:
// Hatalı tanım
#include "hesap.h"
void hesapla() {
}
Bu kod, sınıfın dışında bulunan serbest bir hesapla fonksiyonu tanımlar. Hesap sınıfının bildirdiği üye fonksiyon ise hâlâ tanımsız kaldığı için bağlayıcı, programın istediği Hesap::hesapla sembolünü bulamayabilir.
// Doğru tanım
#include "hesap.h"
void Hesap::hesapla() {
}
Tanımı kontrol ederken başlık dosyasındaki bildirim ile .cpp dosyasındaki tanımı şu açılardan karşılaştır:
- Dönüş türü aynı mı?
- Fonksiyon adı aynı mı?
- Parametrelerin sayısı, türü ve sırası aynı mı?
- Tanımda doğru sınıf kapsamı, yani
Hesap::kullanılmış mı? - Bildirimde
constgibi bir niteleyici varsa tanımda da korunmuş mu?
Harici kütüphane bağlama komutuna nasıl eklenir?

Bir başlık dosyasını #include ile eklemek, kütüphanenin derlenmiş tanımlarını çalıştırılabilir dosyaya kendiliğinden katmaz. Başlık dosyası derleyiciye bildirimleri sağlar; bağlayıcının ayrıca kütüphane dosyasını bulması ve bağlantı komutunda kullanması gerekir.
Harici kütüphane kullanımında şu üç noktayı ayrı ayrı kontrol et:
- Derleyicinin görebileceği başlık dosyası gerçekten mevcut mu?
- Kütüphane dosyasının bulunduğu klasör bağlayıcının arama yollarında mı?
- Kütüphaneyi seçen bağlantı seçeneği komuta eklenmiş mi?
-l<kitaplık-adı> seçeneği bağlayıcıya belirli bir kütüphaneyi aramasını söyler. -L<klasör> ise kütüphane aranırken kullanılacak ek klasörü belirtir. Örneğin nesne dosyaları hazırlandıktan sonra genel komut yapısı şöyle olabilir:
g++ main.o hesap.o -L<klasör> -l<kitaplık-adı> -o uygulama
Burada main.o ve hesap.o içinde kütüphaneden beklenen semboller bulunabilir. Bu nedenle kütüphane seçeneğini, onu kullanan nesne dosyalarından sonra yazmak güvenli bir alışkanlıktır. GNU bağlayıcısı, nesne dosyalarını ve kütüphaneleri komutta verilen sıraya göre işler; -L seçenekleri ise -l ile aranan kütüphaneler için arama yolu ekler.
Kütüphane dosyası bulunamıyorsa veya bağlantı seçeneği eksikse hata mesajında kütüphaneden beklenen semboller için undefined reference to ifadeleri görülebilir. Bu durumda yalnızca başlık dosyasının doğru yazıldığını kontrol etmek yeterli değildir; kütüphanenin bağlantı komutunda seçildiğini ve arama klasörünün doğru verildiğini de incelemek gerekir.
Hata mesajından eksik tanımı bulma yöntemi
undefined reference to ifadesinden sonra görünen sembol adı, bağlayıcının hangi tanımı bulamadığını gösterir. Bağlayıcı, derlenmiş dosyalardaki çağrıları gerçek fonksiyon gövdeleriyle eşleştirir. Bu nedenle önce sembolün serbest fonksiyona, sınıf üyesine veya harici kütüphane işlevine ait olduğunu belirlemek gerekir.
- Mesajdaki sembol adını oku ve fonksiyonun hangi dosyada çağrıldığını belirle.
- Fonksiyon bildirimini, yani adını, parametrelerini ve dönüş türünü yazan satırı ilgili başlık dosyasında bul.
- Fonksiyonun gövdesini içeren tanımı ara. Tanımın gerekli
.cppdosyasında bulunduğunu kontrol et. - Bu
.cppdosyasının derleme komutunda veya gerçek proje yapılandırmasında yer aldığını doğrula. Dosyanın yalnızca klasörde bulunması yeterli değildir. - Sembol bir kütüphane işleviyse ilgili bağlama seçeneğinin komuta eklendiğini kontrol et.
| Belirti | Olası neden | Kontrol |
|---|---|---|
| Fonksiyon bildirimi bulunuyor, fakat tanım bulunamıyor. | Fonksiyon gövdesi yazılmamış veya isim ve parametreler bildirimle eşleşmiyor. | Başlık dosyasındaki bildirim ile .cpp dosyasındaki tanımı karşılaştır. |
| Mesajdaki sembol kendi yazdığın fonksiyona ait. | Yalnızca main.cpp derleniyor olabilir. |
İlgili kaynak dosyasını da derleme komutuna ekle. |
| Sembol bir sınıf üyesi fonksiyonu gösteriyor. | Tanımda Hesap:: gibi sınıf kapsamı öneki unutulmuş olabilir. |
Fonksiyon tanımının sınıf adı ve kapsam çözümleme operatörüyle başladığını kontrol et. |
| Sembol harici bir kütüphaneye ait. | Başlık dosyası eklenmiş, fakat kütüphane bağlama seçeneği verilmemiş olabilir. | Derleme komutundaki kütüphane ve kütüphane yolu seçeneklerini kontrol et. |
Bir IDE kullanıyorsan kaynak dosyanın proje yapılandırmasına gerçekten eklendiğinden emin ol. Dosyayı proje klasörüne kopyalamak, onun otomatik olarak derleneceği anlamına gelmez.
Başlık ve şablon dosyalarında hangi hatalar yapılır?
Başlık dosyasını include etmek, oradaki fonksiyon bildirimini görünür kılar; fonksiyonun tanımını kendiliğinden projeye eklemez. Bildirim, derleyiciye fonksiyonun varlığını ve nasıl çağrılacağını söyler. Tanım ise çalıştırılacak gövdeyi içerir.
// hesap.h
int topla(int a, int b);
// hesap.cpp
int topla(int a, int b) {
return a + b;
}
main.cpp içinde hesap.h ekli olsa bile hesap.cpp derleme komutuna alınmazsa bağlayıcı, topla fonksiyonunun gövdesini bulamaz. Başlık ve kaynak dosyasının aynı klasörde bulunması bu durumu değiştirmez.
Şablon fonksiyonlarda tanımın çoğu zaman şablonun kullanılacağı noktada görünür olması gerekir. Tanım yalnızca bir .cpp dosyasına bırakılır ve başka bir dosyada farklı türlerle kullanılırsa bağlayıcı eksik sembol bildirebilir.
template <typename T>
T kare(T deger) {
return deger * deger;
}
Bu nedenle şablon bildirimi ve tanımı genellikle aynı başlık dosyasında tutulur. Teşhis sırası şöyledir: mesajdaki sembolü oku, bildirimi bul, tanımı ara, sınıf kapsamını kontrol et, ilgili .cpp dosyasını derle ve harici kütüphaneyi bağla. multiple definition of main ise aynı sembolün birden fazla tanımlanmasıyla ilgili farklı bir bağlama hatasıdır.
Sık Sorulan Sorular
Başlık dosyasını include etmek fonksiyonun tanımını da projeye ekler mi?
Başlık dosyasını include etmek çoğunlukla yalnızca bildirimi görünür yapar. Fonksiyon gövdesinin bulunduğu .cpp dosyası ayrıca derlenip bağlanmalıdır.
Neden yalnızca main.cpp dosyasını derlemek undefined reference to hatasına yol açar?
Yalnızca main.cpp derlendiğinde diğer kaynak dosyalarındaki fonksiyon tanımları oluşan programa eklenmez. Bu durumda bağlayıcı, çağrılan fonksiyon için gövde bulamaz.
C++ şablon fonksiyonunun tanımını neden çoğu zaman.cpp dosyasında bırakmamak gerekir?
Şablonun gerçek kodu, kullanıldığı tür bilindiğinde oluşturulur. Tanım görünür değilse derleyici gerekli örneği üretemeyebilir ve bağlama aşamasında eksik sembol oluşabilir.
Harici bir kütüphane kullanırken başlık dosyası ekli olduğu hâlde neden bağlama hatası alınabilir?
Başlık dosyası işlev bildirimlerini sağlar, ancak kütüphanenin derlenmiş kodunu programa eklemez. Bu nedenle gerekli kütüphane bağlama seçeneği ayrıca verilmelidir.
undefined reference to hatasını çözerken mesajdaki sembolü kaynak kodundaki bildirim ve tanımla eşleştirmen, ardından ilgili dosya ile kütüphanenin gerçekten bağlama sürecine katıldığını doğrulaman yeterli bir başlangıç sağlar.