linux下的c 編譯

2021-10-04 14:24:44 字數 4786 閱讀 2756

end目前在linux上,c++的編譯主要依賴於gcc,而gcc實際上並不是乙個編譯器,其全稱為gnu compiler collection,即gnu的編譯器集合,可以通過它來編譯c/c++/object-c等各種語言的原始碼。

其中gcc是gcc中的gnu c compiler(c編譯用),g++即為gcc中的cnu c++ compiler(c++編譯用)。

gcc和g++的主要區別可以檢視這位博主的文章:gcc與gcc,g++區別

材料:裝了helloworld的乙份cpp**

工具:裝了linux和gcc的一台機器

然後直接呼叫命令就可以完成編譯了:

g++ -o hello hello.cpp
其中-o用於指定輸出檔名稱,上面命令中即為hello,而後的引數為需要編譯的原始碼。(不帶-o引數也可,則輸出檔案預設為a.out)

呼叫./hello,大夥兒喜聞樂見的helloworld就列印出來了。

這是最基礎的一種編譯方式,在實際專案中,因為原始碼檔案存在多個,也有需要包含的鏈結庫/標頭檔案,以及指定生成目標為動態鏈結庫/靜態鏈結庫等等,所以會需要一些額外的引數,類似於

-e 引數,普通編譯的第一步,輸出原始檔的預處理結果(載入標頭檔案,巨集替換,條件編譯等),示例:

g++ -e hello.cpp -o hell.e
-s 引數,普通編譯的第二步,執行**中的語法檢查和詞法分析,檢查正確後將其翻譯為彙編**。

g++ -s hello.e -o hello.s
-c 引數,普通編譯的第三步,將彙編**轉換成二進位制**。

g++ -c hello.s -o hello.o
無編譯型別引數,普通編譯的最後一步,將二進位制**與庫檔案等其他檔案鏈結,生成最終目標檔案。(注意這四個步驟中,gcc會自動檢查之前步驟是否完成,若之前的步驟未完成,則自動執行對應的操作)

g++ hello.o -o hello
-o 引數,指定當前編譯過程中產生的目標檔名(就像上面的示例,另外檔案字尾是隨便寫的,linux有啥要求)。

-i 引數(大寫i),指定包含的標頭檔案所在目錄,相對路徑和絕對路徑均可,存在多個目錄時多次新增即可。

g++ hello1.cpp hello2.cpp -o hello -i ./../include1 -i ./../include2
-l 引數,指定鏈結的庫檔案所在目錄,同上。

-l 引數(小寫l),指定鏈結的庫檔名稱,預設lib字首,也不需要字尾,如鏈結liblua.a,寫做-llua即可。一般情況下優先使用靜態庫進行鏈結,若想指定鏈結的庫為靜態庫或動態庫,可使用-l:加上庫檔案全稱即可,如鏈結liblua.a,寫作-l:liblua.a。

g++ hello.cpp -o hello -l ./../lib1 -llua -l ./../lib2 -ljson -l:libtest.so
ar -r hello.a hello.o
值得一提的是,即便hello中存在main函式,對應靜態庫仍能正常生成。因為在其他編譯過程中鏈結該靜態庫時,只會「按需」鏈結。

詳細情況可以檢視這位博主的部落格:自定義函式與靜態庫函式重名不衝突

g++ -o hello.so hello.cpp -shared -fpic
部分系統在使用-shared引數時,是會自動補上-fpic引數的,但也有些系統不會。

所以此處最好顯式標註-fpic項,表明需要讓編譯器產生與位置無關**,即產生的**中全部使用相對位址,不是用絕對位址。

不過某些情況下不加-fpic也能編譯生成動態庫,但條件比較苛刻。(快給我住手這根本就不是動態鏈結)

其他引數讀者可自行查閱資料。

在實際工程中,每次編譯工程時都重新輸入gcc編譯命令顯然過於憨憨。

所以我們需要通過指令碼的形式,簡化編譯這一工作。其中,makefile就是一種用於編譯的特殊指令碼。

makefile的語法規則比較簡單,主要為:

目標:依賴項

命令1(前面固定乙個tab縮排,不能為4個空格)

命令2

比如在上個gcc使用示例中的hello.cpp編譯,我們可以在makefile中這樣編寫:

all:hello

hello:hello.cpp

g++ -o hello hello.cpp

這樣,只需呼叫make all命令,就可以生成對應的hello可執行檔案了。

直接呼叫make命令時,makefile預設只生成第乙個目標檔案即完成編譯,若像上述中呼叫make all,則makefile會編譯指定的目標all。

而目標all的依賴項為hello,makefile就會檢查hello是否存在,或者hello是否存在對應的生成命令。

如此一來,當我們第二次呼叫make all命令時,makefile檢查到hello檔案已存在,而之後也沒有需要執行的命令,就會直接返回。

實際工程中,為了方便重複編譯,還會再增加clean目標:

clean:

rm -fr *.o hello

這樣只需要呼叫make clean即可清除上一次產生的目標檔案與中間檔案,每次編譯的命令即可簡化為make clean;make all。(大家可能注意到清除檔案使用了linux的rm命令,事實上在makefile中是可以直接呼叫linux命令的)

但僅此而已,當工程專案中原始檔較多時,再去乙個個寫每個檔案的名稱也是比較低效的。

對此我們可以使用makefile自帶的字尾規則.***.yyy,gcc會自動將***字尾的檔案視為原始檔,yyy字尾的檔案視為目標檔案,執行統一的生成命令(其中$*代表匹配的檔名中除了字尾的部分):

.cpp.o:

g++ -o $*.o -c $*.cpp

這樣,.cpp字尾的檔案被匹配到時,對應.o字尾的目標檔案生成規則就有了。不過單靠這一條還不能直接直接編譯,因為編譯器需要知道你的原始檔在哪。關於這個,makefile提供了wildcard函式可以用於獲取對應字尾的檔案,再通過makefile的變數替換規則$(var:a=b),將檔案字尾替換為.o,就能得到中間檔案的名稱了。如此一來,可編譯任意個數cpp檔案的makefile就完成了:

src =

$(wildcard *.cpp)

src_obj =

$(src:.cpp=.o)

all:$(src_obj)

g++ -o target.out $(src_obj)

clean:

rm -fr *.o target.out

.cpp.o:

g++ -o $*.o -c $*.cpp

(一般將編譯的一二三步與第四步鏈結拆分為兩批執行,多檔案編譯可通過多執行緒提高編譯速度)

雖然在上面的示例中,makefile的編寫已經存在部分的優化,但某些情況下仍然會存在編譯引數多、亂,無明確歸類的問題。

於是大佬們又想出了通過編寫簡單、規範的cmakelists.txt來生成makefile檔案的方式降低編寫難度。(套娃警告)

我們編寫cmakelists.txt時的主要目標是設定相關編譯引數,剩餘的編譯步驟交由它自動生成。因此你大部分的操作實際上都是在設定各種引數。

如設定c/c++的編譯器set(變數名 對應值):

set(cmake_c_compiler /usr/local/bin/gcc540)

set(cmake_cxx_compiler /usr/local/bin/g++540)

類似於cmake_c_compiler這種變數名都是cmake提前預設的名稱,如果你想設定乙個自定變數,只需要名稱不與他們相同即可。

設定cmake的版本要求(必需項):

cmake_minimum_required(version 2.6)
設定標頭檔案的包含目錄:

include_directories(./../include)
設定目標檔案的編譯引數:

add_definitions(-g -wall -fpic -static-libstdc++ -static-libgcc)
link_directories(./../lib)

link_libraries(lua json)

獲取原始檔,並設定當前的編譯目標為生成庫檔案:

aux_source_directory(./ src)

add_library(target_name shared $

)

aux_source_directory函式用於獲取某個目錄下所有的檔案,用於add_library中(表示原始碼檔案)。而根據其中的shared引數可確認產生的檔案為動態鏈結庫,最終生成的檔案即為target_name.so了。

其find_path/string/exists等自帶函式大家可以自行查閱相關檔案與用法。

關於linux上c++的編譯就暫時寫到這,如果大家有在win平台上vs編譯工程的經驗,應該也能感覺到,linux編譯與win編譯過程中,需要設定的引數都是類似的。

Linux下C編譯的步驟

來自 linuxc程式設計一站式學習 其中main.s是main.c對應的彙編 main.o是目標檔案,a.out是可執行檔案。可以看到圖中每種檔案都有相應的指令來生成a.out可執行檔案。我們以main.c為例,依次生成main.s,main.o.a.out 我們先編寫乙個簡單的.c檔案,儲存為m...

Linux下編譯C 指令

單個原始檔生成可執行程式 下面是乙個儲存在檔案 helloworld.cpp 中乙個簡單的 c 程式的 helloworld.cpp include int main int argc,char argv 程式使用定義在標頭檔案 iostream 中的 cout,向標準輸出寫入乙個簡單的字串。該 可...

Linux下C語言 C 的編譯過程

經過前一段時間的學習,我了解了在linux條件下c語言和c 程式的編譯過程以及所涉及到的編譯使用命令,今天我就來總結一下這些內容。預編譯期的作用 1 刪除注釋 2 處理預處理指令 3 將包含的標頭檔案展開。編譯期的作用 1 語法分析 2 語義分析 3 彙總符號。彙編期的作用 1 將彙編指令翻譯成二進...