天天看點

[工程建構] gcc及編譯相關知識點(管理動态庫搜尋路徑)

指令格式:

指令格式:
gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-Wpedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] [@file] infile...      

前言:

編譯的目的是将代碼轉換成二進制資料以提供給計算機執行,但是從代碼到二進制資料的過程并不是一步就完成的,主要有如下幾個階段:

1)預編譯/預處理:

預編譯指定源檔案,替換其中的宏,将#include語句替換為頭檔案中的内容。

此階段依舊是文本到文本的轉換,輸出結果依舊是文本資料。

指令參數: gcc -E

例:

#預編譯 test.c 檔案,如果test.c中有include語句,那麼取 ./include中查找
#對應的頭檔案,如果找不到,去系統預設的頭檔案路徑中找

#以下語句會把結果輸出到螢幕上
gcc -E -I./include test.c

#轉出輸出結果到檔案中,友善觀察
gcc -E -I./include test.c > 1.txt      

注:直接使用gcc  xxx  -Ixxx  -o  1 這裡的-I其實就是應用在預編譯階段

2)彙編:

彙編是将文本内容翻譯成彙編語言,這個階段依舊沒有生成二進制資料,還是文本的替換,隻不過是進階語言向彙編語言的變更。

指令參數 : gcc -S

例:

#-S選項預設先執行-E,編譯的後一個階段會預設執行前一階段的動作
gcc -S 1.cpp -o 1      

3)編譯:

編譯是将彙編代碼轉換成二進制資料。

指令參數 : gcc -c

例:

gcc -c 1.cpp -o 1.o      

4)連結:

這是工程建構中比較重要的一步,編譯隻是将源碼檔案(.c .cpp 等等)轉換成二進制檔案,但是這些檔案是互相獨立的孤島,他們互相之間僅持有對方的符号表資訊,隻是知道自己需要調用什麼接口,但是并不知道接口對應代碼坐落的具體位址。

連結行為可描述為:“将所有編譯獲得的二進制檔案 和 需要引用的外部庫 歸納到一起,然後為各個二進制檔案中的内容安排好虛拟記憶體位址,最後分析這些檔案中的外部接口調用,把調用正确地指向剛才配置設定的虛拟記憶體位址上,這樣各個二進制檔案中對于其他二進制檔案的函數調用就可以被正确的指向了”。另外這其中也包括了常量資料,靜态資料,全局變量初始化等等的記憶體配置設定。

指令:gcc -o

總而言之:連結唯一的責任就是向虛拟記憶體空間的各個段(.data .text .cdata等)中填資料,再順帶解決下函數調用位址定向問題。

其他:

  • 選項連續和不連續是不一樣的
  • -I後緊跟目錄,指定頭檔案查找目錄,可使用多個-I來指定多個目錄
  • 預編譯-E不生成檔案,如果想預覽預編譯的效果,可重定向gcc -E -I./include test.c > 1.txt
  • -x指定編譯語言,此時無視檔案字尾,可以有gcc -x c 1.txt,此時1.txt中的内容将被當做c語言對待
  • 預處理和編譯-S,生成彙編代碼,彙編代碼可讀
  • -fno-asm,禁止将asm,inline和typeof用作關鍵字
  • -include,如果在檔案中沒有#include <xxx.h>或"xxx.h",而又“不想改代碼”,那麼可以通過此指令在編譯時引入頭檔案
//func.c
     1   void myprintf(void)
     2   {
     3     printf("hello world\n");
     3   }
     gcc -c func.c -include /usr/include/stdio.h -o func.o      
  • -idirafter dir 在-I的目錄裡面查找失敗,将到這個目錄裡面查找.

    -iprefix prefix -iwithprefix dir 一般一起使用,當-I的目錄查找失敗,會到prefix+dir下查找

  • -nostdinc,不再使用預設頭檔案路徑,一般個-I一起使用以精确指定頭檔案位置
  • -C(差別與-c),預處理時不删除注釋,一般和-E一起使用,用以分析程式。和-S一起使用沒效果,隻能和-E一起,生成一個包含頭檔案且有注釋的完整代碼
  • -M,生成檔案關聯的資訊。有點像makefile中的依賴關系
[root@localhost func]# gcc -M func.c -I../include/1
func.o: func.c /usr/include/stdc-predef.h ../include/1/1.h \
 /usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
 /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
 /usr/include/gnu/stubs-64.h \
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/stddef.h \
 /usr/include/bits/types.h /usr/include/bits/typesizes.h \
 /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/stdarg.h \
 /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
 /usr/include/stdlib.h /usr/include/bits/waitflags.h \
 /usr/include/bits/waitstatus.h /usr/include/endian.h \
 /usr/include/bits/endian.h /usr/include/bits/byteswap.h \
 /usr/include/bits/byteswap-16.h /usr/include/sys/types.h \
 /usr/include/time.h /usr/include/sys/select.h /usr/include/bits/select.h \
 /usr/include/bits/sigset.h /usr/include/bits/time.h \
 /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
 /usr/include/alloca.h /usr/include/bits/stdlib-float.h      
  • -MM,作用和-M一樣,但是忽略#include <xxx.h>這種系統預設依賴
  • -MD,和-M一樣,但是會把結果輸出到.d檔案中
  • -MMD,和-MM一樣,但是會把結果輸出到.d檔案中
  • -L,後跟函數庫的路徑,-L../mylib
  • -l,後跟庫的名字,庫需要用lib作為開頭,使用時省略lib
-L和-l要聯合在一起使用,比如我有庫yk,全名叫做libyk.a,如果把其放在預設的庫搜尋目錄中,那麼隻需要使用-lyk即可
 如果libyk.a不在預設的庫搜尋目錄中,那麼就需要用-L指定一下搜尋路徑,即-L/home/yk/ -lyk      
  • -O0 -O1 -O2 -O3 ,編譯器的四個優化級别,-O0表示沒有優化,-O1表示預設優化級别,-O3盡可能多的優化
  • -g,加入調試資訊,加入調試資訊的輸出檔案明顯要大于未加入調試資訊的檔案
  • -static,禁止使用動态庫
  • -share,使用動态庫
  • -w,不生成任何警告
  • -Wall,生成所有警告
  • 檢視lib庫的搜尋路徑 : gcc -print-search-dirs
  • 檢視頭檔案的搜尋路徑:gcc -v -x c -E - (-x c用來指定為c語言,這個指令的最後會輸出include<>和include""的搜尋路徑)
  • 檢視動态庫的搜尋路徑:cat /etc/ld.so.conf

    添加搜尋路徑到預設路徑:vim /etc/ld.so.conf

    編譯時指定臨時動态庫搜尋路徑:gcc -L/home/mylib