C++'ta multiple definition of `main' hatası, aynı çalıştırılabilir hedefe giren dosyalardan birden fazlasında main() tanımı bulunduğunda çıkar. Çözüm için fazladan main()'i kaldır ya da ayrı çalışması gereken dosyayı kendi hedefi olarak, ayrı bir komutla derle. Bir programın giriş noktası olan main() aynı çalıştırılabilir hedef içinde tekil olmalıdır.
Bu hata ne anlıyor? Derleme ve bağlama aşamaları
g++ ile tek komutta program üretmek üç ayrı iş gibi düşünülebilir:
- Derleme: Her
.cppdosyası ayrı ayrı makine koduna çevrilir ve bir nesne dosyası (.o) oluşur. Derleyici o anda yalnızca tek dosyaya bakar. - Bağlama: Bağlayıcı (linker) nesne dosyalarını tek bir çalıştırılabilir dosyada birleştirir. Bir fonksiyonun nerede tanımlandığı burada çözülür.
- Çalıştırma: İşletim sistemi programı başlatır ve
main()'den çalışmaya başlar.
Hata ikinci aşamada çıkar. Her dosya kendi içinde geçerli bir main() içerdiği için derleme sorunsuz geçer. Bağlayıcı nesne dosyalarını birleştirirken main sembolünü iki kez bulur ve hangisini giriş noktası yapacağına karar veremez. Bu yüzden mesaj bir sözdizimi hatası gibi görünmez; tanımların çakışmasından söz eder.
Hatalı iki dosyalı örnek
İki dosyanın ikisinde de main() var:
// ana.cpp
#include <iostream>
int main() {
std::cout << "Ana programn";
return 0;
}
// deneme.cpp
#include <iostream>
int main() {
std::cout << "Denemen";
return 0;
}
İkisini tek hedefte birleştiren komut:
g++ ana.cpp deneme.cpp -o program
GNU ld kullanan bir sistemde çıktı tipik olarak şuna benzer:
/usr/bin/ld: /tmp/ccXXXXXX.o: in function `main':
deneme.cpp:(.text+0x0): multiple definition of `main'; /tmp/ccYYYYYY.o:ana.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Tam metin araç zincirine, platforma ve bağlayıcının biçimine göre değişir. Geçici dosya adları, yol ve satır sırası sende farklı görünebilir. Değişmeyen parçalar multiple definition of `main', first defined here ve en sonda bağlayıcının hata koduyla çıktığını bildiren satırdır.
first defined here ifadesi, bağlayıcının sembolü ilk gördüğü dosyayı gösterir. Yukarıdaki komutta ana.cpp önce verildiği için ilk tanım odur. multiple definition satırının başında yer alan dosya ise çakışan ikinci tanımı taşır, yani burada deneme.cpp. İki dosyadan hangisinin gerçekten gereksiz olduğunu mesaj söylemez; bunu projenin amacına bakarak sen karar verirsin.
Aynı hedefe ikinci bir main() nasıl girer? Sık nedenler

İki dosyayı bilerek aynı komuta yazmak nadirdir. Hata çoğunlukla ikinci main()'in fark edilmeden hedefe girmesiyle çıkar. Üç sık neden var.
1. Unutulan deneme ya da test dosyası
Bir fonksiyonu denemek için klasöre deneme.cpp açıp içine kısa bir main() yazarsın, sonra dosyayı silmeyi unutursun. Klasördeki bütün kaynakları derleyen komut onu da hedefe katar:
g++ *.cpp -o program
Joker karakter (*) o klasördeki tüm .cpp dosyalarını komuta yazar. Yani aklında olmayan dosya da bağlamaya girer.
2. IDE projesine eklenen ikinci dosya
Çoğu geliştirme ortamı proje içindeki bütün kaynak dosyaları tek bir çalıştırılabilir hedef olarak derler. Projeye "yeni dosya ekle" ile ikinci bir dosya ekleyip içine yeni bir main() yazarsan, ortam bunu ayrı bir program saymaz, mevcut hedefe ekler. Ayrı çalışacak her main() için ayrı proje ya da ayrı hedef gerekir.
3. .cpp dosyasını #include ile içeri almak
Başlık dosyası yerine kaynak dosyasını içeri aldığında, aynı tanım iki kez derlenir:
// yardimci.cpp
int topla(int a, int b) {
return a + b;
}
// ana.cpp
#include "yardimci.cpp" // tanım burada metin olarak kopyalanır
int main() {
return topla(2, 3);
}
Bu iki dosyayı g++ ana.cpp yardimci.cpp -o program ile derlediğinde topla hem ana.cpp'nin içinde hem yardimci.cpp'nin kendisinde bulunur. Bağlayıcı bu kez multiple definition of `topla(int, int)' benzeri bir mesaj verir. Aynı mantık main() için de geçerlidir: main() içeren bir .cpp dosyasını #include ile başka dosyaya alırsan, o dosya ayrıca derlendiğinde de ikinci kez girmiş olur.
Üç aşamalı teşhis: g++ -c, nm ve bağlama
Hatanın hangi dosyalardan geldiğini tahmin etmek yerine derleme ile bağlamayı ayır ve her aşamayı ayrı çalıştır. Aşağıdaki örnekte ana.cpp ve deneme.cpp dosyalarının ikisinde de main() var.
- Her dosyayı tek başına derle.
Beklenen sonuç: hata yok, çıktı sessiz. Klasördeg++ -c ana.cpp g++ -c deneme.cppana.ovedeneme.ooluşur. Derleme aşaması her dosyaya ayrı baktığı için bu noktada çakışma görünmez. - Nesne dosyalarında main sembolünü ara.
Linux'ta beklenen çıktı iki dosyada da şuna benzer:nm ana.o | grep main nm deneme.o | grep main0000000000000000 T mainTharfi, sembolün kod bölümünde tanımlı ve dışarıya açık olduğunu gösterir. Büyük harf sembolün global olduğunu, küçük harf çoğunlukla yerel olduğunu anlatır. Yani iki dosya damain'i tanımlıyor. Adres sütunu ve biçim platforma göre değişir. macOS gibi bazı sistemlerde sembol başında alt çizgiyle_maingörünebilir. - Nesne dosyalarını bağla.
Beklenen sonuç, kelimesi kelimesine aynı olmasa da şuna benzer bir hatadır:g++ ana.o deneme.o -o program/usr/bin/ld: deneme.o: in function `main': deneme.cpp:(.text+0x0): multiple definition of `main'; ana.o:ana.cpp:(.text+0x0): first defined here collect2: error: ld returned 1 exit status
Sonucu nasıl yorumlarsın?
İki nesne dosyasında da T main görüyorsan sorun bağlama aşamasındadır ve fazladan bir main() vardır. İkisinde de görünmüyorsa başka bir hatayla uğraşıyorsun demektir. undefined reference to `main' hatası bunun tersidir: hiçbir nesne dosyasında main yoktur, bağlayıcı giriş noktasını bulamaz. "Multiple definition" fazlalığı, "undefined reference" eksikliği bildirir.
Çözüm: fazladan main()'i kaldır ya da test dosyasını ayrı hedef yap

Kural şu: her çalıştırılabilir hedefte tek main() olur. İki yol var.
(a) Deneme dosyasına ihtiyacın yoksa dosyayı sil ya da bağlama komutundan çıkar. Komutta deneme.cpp yoksa ikinci main da programa girmez.
(b) Deneme ya da test dosyasının kendi main()'i gerekiyorsa onu ayrı bir çalıştırılabilir dosya olarak derle. Ortak kodu ise ayrı bir .cpp dosyasına taşı:
src/
hesap.h (topla bildirimi)
hesap.cpp (topla tanımı, main yok)
ana.cpp (kendi main'i)
test.cpp (kendi main'i)
Ortak kod hesap.h ve hesap.cpp dosyalarında durur. ana.cpp ve test.cpp ise #include "hesap.h" yazar. Bir .cpp dosyasını #include ile başka dosyaya eklemek, kodu iki yerde birden tanımlayıp bu hatayı geri getirebilir. Paylaşılacak şeyi header'a koy, tanımı tek bir .cpp dosyasında tut.
Derleme komutları:
g++ ana.cpp hesap.cpp -o program
g++ test.cpp hesap.cpp -o test
./program
./test
Beklenen sonuç: hata yok, iki ayrı çalıştırılabilir dosya oluşur. Örnek çıktılar:
Toplam: 5
Test gecti
Her komutta yalnızca bir dosyada main() var, hesap.cpp ise ikisinde de ortak olarak kullanılıyor. Bu yüzden bağlayıcı çakışma görmez.
CMake ya da bir IDE kullanıyorsan aynı mantık geçerlidir: her çalıştırılabilir hedef ayrı tanımlanır. CMake'te bu, ayrı add_executable satırlarıdır:
add_executable(ana_program src/ana.cpp src/hesap.cpp)
add_executable(hesap_testi src/test.cpp src/hesap.cpp)
Bu iki satır, kaynak dosya listelerini ayrı tuttuğu için iki hedefin birbirinin main()'ine karışmasını önler.
Header ve source ayrımı: başlıkta tanım yazınca da "multiple definition" çıkar
Aynı hata ailesi main() dışındaki fonksiyonlarda da görülür. Kök neden aynıdır: bir fonksiyonun tanımı, çalıştırılabilir dosyayı oluşturan nesne dosyalarından birden fazlasında bulunur. Başlık dosyasına (.h) yalnızca bildirim yazılır. Tanım ise tek bir .cpp dosyasında durur.
Aşağıdaki başlık dosyası fonksiyonun tanımını içerdiği için hatalıdır:
// hesap.h (hatalı: tanım başlıkta)
#ifndef HESAP_H
#define HESAP_H
int topla(int a, int b) {
return a + b;
}
#endif
main.cpp ve olcum.cpp dosyalarının ikisi de #include "hesap.h" satırını içeriyor olsun. g++ main.cpp olcum.cpp -o program komutu şuna benzer bir çıktı verir:
/usr/bin/ld: olcum.o: in function `topla(int, int)':
olcum.cpp:(.text+0x0): multiple definition of `topla(int, int)'; main.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Önişlemci her .cpp dosyasında başlığı ayrı ayrı metne ekler. Böylece iki nesne dosyasının ikisi de topla fonksiyonunun kendi kopyasını taşır. Bağlayıcı bu iki kopyayı görünce hata verir.
Çözüm 1: Tanımı .cpp dosyasına taşı
// hesap.h
#ifndef HESAP_H
#define HESAP_H
int topla(int a, int b); // yalnız bildirim
#endif
// hesap.cpp
#include "hesap.h"
int topla(int a, int b) {
return a + b;
}
Derlerken hesap.cpp dosyasını da komuta eklemelisin: g++ main.cpp olcum.cpp hesap.cpp -o program. Tanım artık yalnızca hesap.o içinde olduğundan çakışma kalmaz.
Çözüm 2: Fonksiyonu inline yap
// hesap.h
#ifndef HESAP_H
#define HESAP_H
inline int topla(int a, int b) {
return a + b;
}
#endif
C++ kurallarına göre inline fonksiyon, birden fazla çeviri biriminde bulunabilir. Tek koşul, tanımların birebir aynı olmasıdır. Bu nedenle kısa yardımcı fonksiyonlar ve şablonlar başlıkta durabilir. Burada inline anahtar sözcüğünün asıl işi, derleyiciye "satır içi aç" demek değil, tanımın birden fazla dosyada bulunmasına izin vermektir.
Include guard bu hatayı neden çözmez?
#ifndef ile yazdığın include guard, başlığın aynı .cpp dosyası içinde iki kez eklenmesini engeller. Ancak her .cpp dosyası ayrı bir derleme birimidir ve guard her birinde baştan işler. Bu yüzden guard, farklı dosyalardaki kopyaları engellemez.
Son bir ayrım: multiple definition tanımın fazla olduğunu, undefined reference to main ise hiç main() bulunmadığını söyler. İkisi zıt yönlü hatalardır.
Çok dosyalı projede hızlı kontrol listesi
- Derleme komutundaki ya da IDE hedefindeki
.cppdosyalarına bak. Hedefte olmaması gereken bir deneme ya da test dosyası var mı? - Proje klasöründe
grep -n "main" *.cppkomutunu çalıştır. Aynı hedefe giren dosyalardan yalnızca birindeint main()görünmeli. #include "dosya.cpp"yazma. Kaynak dosyaları#includeile değil, derleme komutuyla birleştir.- Başlıkta yalnızca bildirim,
inlinefonksiyon, şablon ve sınıf tanımı bulundur. Sıradan fonksiyon gövdelerini.cppdosyasına yaz. - Deneme ve test kodlarını ayrı bir hedefte tut, ayrı bir çalıştırılabilir dosya olarak derle.
- Hata alırsan önce
g++ -cile her dosyayı ayrı derle. Derleme geçip hata bağlama aşamasında çıkıyorsa sorun çoğunlukla tanım sayısındadır.
Derleyicinin işi kodunu nesne dosyasına çevirmek, bağlayıcının işi bu nesne dosyalarını tek programda birleştirmektir. Bu ayrımı bilince hata mesajları da daha kolay okunur: error: ve satır numarası görüyorsan derleme aşamasındasın, ld returned 1 exit status ve first defined here görüyorsan bağlama aşamasındasın. Giriş noktası kavramı başka dillerde de karşına çıkar, bu yüzden aynı mantık orada da işine yarar.
Sık Sorulan Sorular
Aynı projede birden fazla main() olması hiç mümkün değil mi?
Mümkün, ama her main() ayrı bir çalıştırılabilir hedefe ait olmalı. Bir klasörde birden fazla .cpp dosyası ve her birinde main() bulunabilir. Her biri ayrı bir komutla, ayrı bir program olarak derlenirse hata çıkmaz. Hata, bunlar aynı çalıştırılabilir dosyaya birlikte bağlanınca çıkar.
"first defined here" satırı neyi gösterir?
Bağlayıcının aynı sembolü ilk gördüğü nesne dosyasını ve konumu gösterir. Hata mesajının öbür yarısı, çakışan ikinci tanımı taşıyan dosyayı verir. İki dosyadan birindeki tanımı kaldırman ya da dosyalardan birini hedeften çıkarman gerekir.
#include ile .cpp dosyası eklemek neden multiple definition hatasına yol açar?
#include dosyanın içeriğini olduğu gibi metne yapıştırır. Bir .cpp dosyasını eklersen onun fonksiyon tanımları da eklediğin dosyanın içine girer. O .cpp dosyasını ayrıca derleme komutuna da yazdıysan aynı tanım iki nesne dosyasında bulunur ve bağlayıcı hata verir.
Bu hatayı gördüğünde önce kaç tanım olduğuna, sonra bu tanımların hangi dosyalarda bulunduğuna bak. Çoğu durumda çözüm, fazla tanımı kaldırmak ya da dosyayı doğru hedefe taşımaktır.