2013-03-18 107 views
0

我有一個hello world cpp文件。如果我編譯c++ test.cpp -o test,我得到可執行的「test」文件(-rwxr-xr-x),如果我執行它,它會被執行併產生預期的結果。爲什麼可執行文件不可執行?

但是,如果我使用${CXX} -std=c++0x -I${INCLUDE_DIR1} -c test.cpp -o test -L{LIB_DIR1} -llib_name我也會得到「測試」文件,但在這種情況下它不可執行。所以,我無法執行它。我試圖chmod +x,它獲取權限被執行,但如果我嘗試執行它會得到一個錯誤消息(無法執行)。

我在做什麼錯誤以及如何糾正?

+0

'文件測試',看看它是什麼類型 - 可能只是一個錯誤的.o文件。 – 2013-03-18 14:48:03

+0

如果我輸入'file test',我會得到'test:ELF 64位LSB可重定位,x86-64,版本1(GNU/Linux),不會被剝離「,但我不知道它是什麼意思。這個輸出是否意味着我有一個「錯誤的.o文件」? – Roman 2013-03-18 15:02:06

回答

18

-c告訴編譯器不是生成一個可執行文件(它的意思是「僅編譯」)。它只創建一個對象文件,適合鏈接到可執行文件(可能與其他對象文件和庫)。

如果您需要可執行文件,請刪除-c開關。

有關完整的編譯過程的詳細信息,請參閱:How does the compilation/linking process work?

+0

如果我刪除-c,我會得到:'/ usr/bin/ld:找不到-llib_name collect2:error:ld返回1退出狀態。 – Roman 2013-03-18 15:03:29

+0

這只是意味着編譯器無法找到該庫。如果它不在默認搜索路徑中,則需要使用'-L/path/to/lib'指定它所在的目錄。 – Mat 2013-03-18 15:05:59

1

好像你在這個整體規劃瘋狂的開始。所以,如果你很難理解什麼是錯誤命名的對象(在本例中稱爲.o)文件,編譯器參數以及它們實際做了什麼,甚至是庫名稱,我都明白。

我的假設是,從它的外觀來看,你正在複製你正在執行的shell腳本行,以便從一個未知的例子中產生你的可執行文件(至少使其感覺如此)。所以,我會盡量簡單,清楚,粗略地解釋這裏發生的一切。

首先,C++編譯器將給定的源文件編譯爲可重定位的機器碼。如果將編譯器(和編譯器)的排放存儲在文件中,則該文件將成爲前述的目標文件(請記住.o文件)。如果你注意了,這需要一個目標文件包含可重新定位的機器代碼

現在,您已經編譯了您的源代碼,並且您的機器代碼等同於存儲在目標文件中。但是,這個目標文件不是直接可執行的;即使它確實包含CPU應該沒有執行問題的機器代碼。問題是,這個機器碼沒有鏈接。因此,第二個要點是:目標文件中的可重定位機器代碼未鏈接,因此不能執行。除此之外,目標文件還可能包含其他元數據以幫助鏈接程序處理鏈接過程,從而生成實際的可執行文件。我們將調用整個這個中間表示的對象代碼。因此,現在我們編譯了我們的源代碼併發送了目標文件,下一個顯而易見的邏輯步驟就是將目標文件與任何想要生成可執行文件的庫和/或其他目標文件鏈接起來我們可以像您的第一個./test二進制一樣執行。這是鏈接器起作用的地方,鏈接器接受目標文件和庫(注意:粗略地說,庫是目標文件的集合),並執行一些鏈接魔術,例如解析跨模塊的對象文件中的未定義引用,以及排列可執行文件等然後,鏈接器發出符合鏈接器被調用的目標平臺的可執行文件格式的最終​​可執行文件。這是發射的這個文件,你可以像./test那樣執行。

所以,現在你已經知道了基礎知識,讓我們看看你的shell調用有什麼問題。首先,你應該知道通過調用c++你可以觸發C++編譯器(我認爲它是g++clang++在這種情況下)鏈接器。其次,您應該知道-c標誌告訴編譯器編譯給定的源文件,並單獨發出其等效的目標代碼和機器碼。因此,調用C++編譯器而不使用-c標誌不僅會導致它編譯給定的源代碼,而且還會鏈接生成的目標代碼並生成最終的可執行文件。這是你的第一個命令行中發生的事情。發出的文件名爲test是鏈接的可執行文件。但是,在您的第二個命令行中存在-c標誌。在這種情況下,編譯器將只發布編譯爲test.cpp的目標代碼。您需要鏈接此生成的目標文件才能生成可執行文件。看起來你告訴編譯器將它生成的目標代碼存儲在一個名爲test的文件中,這是Marc在提到錯誤行爲(目標文件通常具有.o.obj後綴)時講述的內容。

從此時起,您有兩種選擇,可以刪除-c標誌或鏈接目標文件。編譯和鏈接(單獨)的目標文件(包括可能-IT-shouldn't待有™liblib_name):

${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -c test.cpp -o test.o 
${CXX} -std=c++0x -stdlib=libc++ -L${LIB_DIR1} -llib_name test.o -o test 

我已介紹libc++那裏的自由。如果你願意使用C++ 11(你把它稱爲C++ 0x),那麼它會更好。但是,如果你想編譯和一個單線聯繫,同樣的事情可以用這個來實現:

${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -L${LIB_DIR1} -llib_name test.cpp -o test 

這兩個做同樣的事情。但請注意,單線版本缺少-c標誌。

現在我們幾乎涵蓋了最後一個問題,那就是-I,-L-l做什麼。 -I指定編譯器將搜索包含在源代碼中的頭文件的路徑。此路徑不會覆蓋編譯器的默認標題搜索路徑,它只是作爲它們的補充。 -L是一樣的東西,但圖書館。您的編譯器將在此路徑中查找您明確鏈接的庫(例如在此例中爲liblib_name)。同樣,這不會覆蓋默認庫搜索路徑。最後,-l標誌用於指定鏈接器在鏈接可執行文件時鏈接的庫。在你的情況下,你鏈接到一個名爲liblib_name的庫(-l標誌的名稱省略了庫的文件名中的第一個lib),你說你的編譯器抱怨這個庫不存在。你確定你真的想將liblib_name鏈接到你的可執行文件嗎?這是一個真正的圖書館嗎?如果沒有,那麼刪除整個-llib_name參數,你的編譯器應該停止對它的大喊大叫。

對不起,很長,很長,很長的解釋,但我希望你在這裏學會了一個小點,並在那裏輕咬,所有這些對你有用。

相關問題