Linux下的C/C++開發基礎(編寫makefile、編譯C/C++、鏈接、可執行程序)

本文重點介紹C/C++源碼工程的編譯鏈接,編譯器gcc/g++的安裝配置略過...
1. 安裝配置gcc g++
2. 創建文件 test.h /test.c / file.h  / file.cpp 
3. 編譯.o庫: gcc -c / g++ -c 
   鏈接生成靜態庫.a庫: ar -r
   鏈接生成動態庫.soK庫:gcc -shared -o/ g++ -shared -o
   編譯可執行程序並鏈接.a或.so 庫 g++ -o file file.cpp -L. -ltest / gcc -o test test.c -L. -lfile
   運行可執行程序 ./file  / ./test
   note:l指定庫名稱,優先鏈接so動態庫,沒有動態庫再鏈接.a靜態庫;
         如果鏈接的是靜態庫就可以直接運行了,如果鏈接的是動態庫可能會提示:./cppmain: error while loading shared libraries: libCAdd.so: cannot open shared object file: No such file or directory,是因爲Linux系統程序和Windows不一樣,Linux系統只會從系統環境變量指定的路徑加載動態庫,可以把生成的動態庫放到系統目錄,或者執行export LD_LIBRARY_PATH=./設置當前路徑爲系統鏈接庫目錄就可以了。


3.1 編譯 C調C++庫
    a. 編譯C++ .o 庫:                g++ -c file.cpp
    b. 鏈接生成靜態 .a庫:             ar -r libfile.a file.o
    c. 編譯C文件並鏈接 .a庫生成可執行程序:    gcc -o test test.c -L. -llibfile
    d. 運行可執行程序:                ./test

3.2 編譯C++調C庫
    a. 編譯C .o庫:                gcc -c test.c
    b. 鏈接生成靜態.a 庫:            ar -r libtest.a test.o
    c. 編譯C++文件並鏈接.a 庫生成可執行程序:     g++ file file.cpp -L. -llibtest
    d. 運行可執行程序:                ./file

4. 查看符號表:
$ nm test.o
0000000000000000 T test
$ nm test1.o
0000000000000000 T _Z4testiPc
C語言編譯後的函數符號還是原函數名,而C++編譯後的函數符號由test變成了_Z4testiPc,test前面有個數字4應該是函數名長度,test後面i Pc應該就是函數的參數簽名。C++之所以這樣規定編譯後的函數符號是因爲對面對象的C++具有函數重載功能,以此來區分不同的函數。

5. linux下的.o .a .so
.o,是目標文件,相當於windows中的.obj文件 
.a爲靜態庫,是好多個.o合在一起,用於靜態連接
.so 爲共享庫,是shared object,用於動態連接的,相當於windows下的dll 
程序的運行都要經過編譯和鏈接兩個步驟。假如文件test.c,使用命令gcc -c test.c進行編譯,生成test.o中間文件,使用命令ar -r libtest.a test.o可以生成libadd.a靜態庫文件。靜態庫文件其實就是對.o中間文件進行的封裝,使用nm libtest.a命令可以查看其中封裝的中間文件以及函數符號。 
鏈接靜態庫就是鏈接靜態庫中的.o文件,這和直接編譯多個文件再鏈接成可執行文件一樣。
動態鏈接庫是程序執行的時候直接調用的“插件”,使用命令gcc -shared -o libtest.so test.c生成so動態庫。
動態庫鏈接的時候可以像靜態庫一樣鏈接,告訴編譯器函數的定義在這個靜態庫中(避免找不到函數定義的錯誤),只是不把這個so打包到可執行文件中。如果沒有頭文件的話,可以使用dlopen/dlsum函數手動去加載相應的動態庫


6.linux下的Makefile編寫:
6.1 純C或純C++文件:
test: test.o test1.o test2.o
    gcc test.o test1.o test2.o -o libtest  #-o鏈接成可執行程序
test.o: test.c  
    gcc -c test.c -o test.o  #加-c 指定生成爲可重鏈接.o文件
test1.o: test1.c
    gcc -c test1.c -o test1.o
test2.o: test2.c
    gcc -c test2.c -o test2.o

.PHONY:clean
clean:
    -rm -rf *.o libtest

每個命令行前必須要有tab符號(如Makefile中書寫方法);
上面的Makefile文件就是要編譯出一個libtest的可執行文件;
test:test.o test1.o test2.o :test依賴於test.o test1.o、test2.o三個目標文件;
gcc test.o test1.o test2.o -o libtest:編譯出可執行文件libtest, -o表示指定可執行文件的名稱;
test.o: test.c: test.o 依賴於 test.c 文件;
gcc -c test.c -o test.o 編譯出test.o文件,-c表示只把給它的文件編譯成目標文件,用源碼文件的文件名命名,但把其後綴由“.c”變成“.o”;也可以省略 -o test.o ,編譯器默認生成test.o
clean:
rm -rf *.o firstTest:當鍵入make clean的時候,會刪除所有.o文件和libtest文件
如果要編譯c++文件,把gcc改成g++即可。如果一行寫的內容過多,可以用“\”來分解多行,注意“\”後面不加空格.

6.2 C/C++混合編譯:

CC = gcc
C++ = g++
LINK = g++

#LIBS = -lz -lm -lpcre(#如果還要鏈接其他庫如libz、libm、libpcre等,加上這句)
#must add -fPIC option
CCFLAGS = $(COMPILER_FLAGS) -c -g -fPIC
C++FLAGS = $(COMPILER_FLAGS) -c -g -fPIC

TARGET=libtestmain

INCLUDES = -I. -I../../

C++FILES = testmain.cpp 
CFILES = test.c test1.c test2.c

OBJFILE = $(CFILES:.c=.o) $(C++FILES:.cpp=.o)

all:$(TARGET)

$(TARGET): $(OBJFILE)
    $(LINK) $^ $(LIBS) -Wall -fPIC (編so加 -shared)-o $@

%.o:%.c
    $(CC) -o $@ $(CCFLAGS) $< $(INCLUDES)

%.o:%.cpp
    $(C++) -o $@ $(C++FLAGS) $< $(INCLUDES)

install:
    tsxs -i -o $(TARGET)

clean:
    rm -rf $(TARGET)
    rm -rf $(OBJFILE)

上面的Makefile是實現編譯C文件編譯成.o,然後一起連接到cpp編譯的.o(或.so)上編譯出可執行文件;
note:
a.如果LIBS的位置放置不對,這幾個基礎庫將不會編進so中。LIBS只應該在最後鏈接爲so時才調用,前面編譯c和cpp文件時用不到。
b.c源文件放到CFILES宏後面, cpp文件放到C++Files宏後面,第三方庫放到LIBS宏後面,頭文件的包含路徑放到INCLUDES後面,庫文件的包含路徑放到使用-L./等表達式放到LIBS中的開頭即可。
c.這裏嚴格區分c和cpp文件的目的是,c文件使用gcc編譯,而cpp文件會使用g++編譯,它們必須嚴格區分開。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章