2016-11-25 57 views
1

我正在做一個示例演練,在我用來學習的教科書中。所有我需要做的是編譯,鏈接並運行以下3個文件:g ++無法將.o文件鏈接到可執行文件中

//file my.h 
extern int foo; 
void print_foo(); 
void print(int); 

my.h是聲明兩個函數和一個「全球」廉政富,無初始值的簡單的頭文件。

//file my.cpp 
#include "my.h" 
#include "std_lib_facilities.h" //not included but not source of error 

void print_foo() 
{ 
    cout << foo << endl; 
} 

void print(int i) 
{ 
    cout << i << endl; 
} 

my.cpp包含從my.h包含的函數的實現。 std_lib_facilities.h是教科書中的一個文件,不是錯誤的根源(根據g ++)。如果需要,我可以將其編輯到問題的正文中。

//file use.cpp 
#include "my.h" 
#include <iostream> 

int main() { 
    foo = 7; 
    print_foo(); 
    print(99) 

    char cc; cin >> cc; 
    return 0; 
} 

use.cpp作爲這一計劃的主要執行文件,並嘗試使用所有這三個聲明&定義的對象。

我採用了兩步命令的方法來構建使用g ++。首先,我編譯了兩個.cpp文件:

g++ -c my.cpp use.cpp 

它創建了兩個目標文件my.o和use.o.我用下面的命令將它們連接:

g++ -o myprog my.o use.o 

給我這個錯誤:

Undefined symbols for architecture x86_64: 
    "_foo", referenced from: 
     print_foo() in my.o 
     _main in use.o 
    (maybe you meant: __Z9print_foov) 
ld: symbol(s) not found for architecture x86_64 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

我試圖把

int foo; 

到my.h而不是

extern int foo; 

這給了我同樣的錯誤。

我已經使用

-std=c++11 

標誌以及這就造成了同樣的錯誤嘗試。

我正在使用帶有最新macOS(實際上已更新)的MacBook Pro,如果這有助於解釋錯誤消息。

我試圖初始化foo,它並沒有改變任何東西。

另外,我試過更新命令行工具,同樣的錯誤。

從我所瞭解的錯誤告訴我,即使my.h包含在兩個文件中,儘管它明確聲明,但實際上它們都不能使用foo變量(它調用_foo)實現任何函數在my.h中。我的猜測是鏈接器在引擎蓋下使用了錯誤的名稱,這使得無法鏈接到可執行文件。這來自錯誤提及的一個事實

__Z9print_foov 

它在任何文件中都不存在。

在這一點上,它幾乎看起來像一個g ++或macOS /命令行工具錯誤。我不想每次都添加聲明,因爲無論如何這會造成重複的符號錯誤。把我的.cpp和使用。cpp到一個文件可能會鏈接正確,但我需要確保我可以實際鏈接多個cpp文件,因爲我最終(希望)會與需要鏈接的多個cpp文件一起工作。任何幫助表示讚賞!

回答

1

在這裏,您聲明一個變量:

extern int foo; 

和使用變量:

cout << foo << endl; 

,但你沒有任何地方定義變量。鏈接器錯誤指出鏈接器找不到變量的定義。要解決此問題,請將文件範圍int foo;置於.cpp文件之一中。

在這個問題你說,改變extern int foo;int foo;給出了同樣的錯誤。但是,如果您仔細查看錯誤消息,我認爲您會發現它提供了另一個關於多個定義的錯誤消息。

1

我建議在命令g++ -Wall -c my.cpp(即給人以my.o)和g++ -Wall -c use.cpp(給use.o)進行編譯,然後用g++ my.o use.o -o myprog鏈接的程序。其實你應該寫一個Makefile(見this靈感)和簡單的運行make

你的翻譯單元my.cppuse.cpp都是聲明一些extern int foo;變量,它是從未定義。所以,你需要定義一個單個文件(而不是在別人!),可能是通過增加(到my.cpp單獨例如)

int foo; 

(不extern),甚至與一些明確的初始值例如int foo = 34;

This comes from the fact that the error mentioned a __Z9print_foov which exists nowhere

它是一種mangled name,這是在兩個目標文件引用(但不限定)(另見this)。

It almost seems like a g++ or macOS/Command Line Tools bug at this point

你是不太可能找到編譯工具的錯誤(這兩個GCC & Clang/LLVM非常好測試,因爲它們多萬條免費的軟件,他們確實有殘留錯誤,但你有在抽獎中獲勝的機會多於受編譯器錯誤影響的機會)。我從1974年開始編碼,在我有生之年只發生過一次。一個更現實的態度就是要更謙虛,而問題你自己的代碼(和知識)之前懷疑編譯器或構建鏈。順便說一下,總是先編譯所有的警告和調試信息(例如g++ -Wall -g,也可能是-Wextra)。使用gdb調試器。當你確信你的代碼沒有錯誤時,你可以通過詢問編譯器optimize(因此使用g++ -Wall -O2-g進行編譯)來對它進行基準測試。請參閱linker wikipage。潛入您的C++ textbook(也參見this siteC++11標準,例如n3337草案)至理解聲明和限定一些變量或函數之間的差。您通常會在某個常見標題中聲明全局變量extern(包含在幾個翻譯單元中),並在其他地方將其定義爲一次,但最佳做法是避免擁有大量全局變量。另請參閱C++17新的inline變量。

+0

Declared int foo;在my.cpp中;有效!感謝您的幫助。但是我不明白爲什麼鏈接器有問題,但是如果變量是在頭文件中聲明的,因此在包含它的.cpp文件中聲明(除非我丟失了某些東西)。如果我不得不在.cpp文件中再次聲明它,那麼在頭文件中聲明extern int foo有什麼意義?爲了約定?我不認爲這個extern做了什麼特別的事情,但仍然很好奇,以至於我再也沒有這個問題。 – gasoline

+0

閱讀更多關於聲明變量和定義變量之間差異的C++書籍。 –

+0

夠公平的。這個bug的評論比其他任何內容都更令人沮喪,並不打算破壞gcc的努力。會試着更謙虛一點。感謝您花時間給我建議,而不僅僅是回答問題!我讀了一些文檔,並意識到即使沒有任何初始化值,plain int foo也被視爲一個聲明和一個定義,而頭部中的extern int foo聲明沒有定義,因此未定義,導致我的錯誤。 – gasoline