命令格式:
命令格式:
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