“-Wall”選項打開所有最常用到的編譯警告,強烈建議打開,可以捕捉到許多在C編程中最常發生的錯誤。
“-o”選項來為可執行文件指定一個不同的輸出文件。
“-c”用於把源碼文件編譯成對象文件。
對象文件包含的是機器碼,其中任何對在其他文件中的函數(或變量)的內存地址的引用都留着沒有被解析。這樣就允許在互相之間不直接引用的情況下編譯各個源代碼文件。鏈接器在生成可執行文件時會填寫這些還缺少的地址,然后把所有的對象文件組合在一起生成單個的可執行文件。當用“-c”來編譯時,編譯器會自動生成與源文件同名,但用“.o”來代替原來的擴展名的對象文件。
gcc使用鏈接器ld來施行鏈接,它是一個單獨的程序。在GNU系統上用到的是GNU的鏈接器,即GNU ld。
通常,鏈接要快於編譯----在一個有許多源文件的大型項目中,只重新編譯那些被修改過的文件可以顯著地節省時間。僅僅重編譯項目中修改過的文件的過程可以用GNU Make來自動完成。
標准的系統庫通常能在“/usr/lib”和“/lib”目錄下,C標准庫自身存放在“/usr/lib/libc.a”中,包含ANSI/ISO C標准指定的各個函數。其他庫都需要顯示或隱示指定。
“-lNAME”試圖鏈接標准庫目錄下的文件名為“libNAME.a”中的對象文件。在大型程序中通常會用到很多“-l”選項,來鏈接象數學庫,圖形庫和網絡庫。使用選項“-lNAME”的情況下,靜態庫“libNAME”可以用於鏈接,但編譯器首先會檢查具有相同名字和“.so”為擴展名的共享庫。默認情況下,載入器僅在一些預定義的系統目錄中查找共享庫,比如“/usr/local/lib”和“/usr/lib”。如果庫不在這些目錄中,那它必須被添加到載入路徑(load path)中去。設置載入路徑的最簡單方法是通過環境變量LD_LIBRARY_PATH。
“-static”選項可以迫使gcc靜態鏈接,避免使用共享庫。
使用庫文件,為了得到函數參數和返回值正確類型的聲明,必須包括入相應的頭文件。如果沒有函數聲明,可能傳遞錯誤類型的函數參數,從而導致不對的結果。
默認情況下,gcc在下面目錄中搜索頭文件:
/usr/local/include/
/usr/include/
在下面目錄中搜索庫:
/usr/local/lib/
/usr/lib/
“-I”用於把新目錄添加到include路徑上。例如,-I/opt/gdbm-1.8.3/include,注意,不應該在源代碼中的#include語句中放入頭文件的絕對路徑,因為這會讓該程序不能在其他系統上編譯。“-I”選項或下面就要介紹的INCLUDE_PATH變量用來設置頭文件的include路徑,一般IDE都有設置包含include路徑的選項。
“-L”用於把新目錄添加到庫搜索路徑上。例如,-L/opt/gdbm-1.8.3/lib。
“-ansi”禁止那些與ANSI/ISO標准沖突的GNU擴展特性。
“-std”選項來控制GCC編譯時采用的某個C語言標准。
‘-W’這是一個類似“-Wall”的通用選項,它對a selection of常見編程錯誤產生警告。“-W”和“-Wall”選項通常同時使用。
“-DNAME”選項在命令行上定義預處理宏NAME,默認情況下,其值為1。“-D”命令行選項可以用來定義有值的宏,形式是“-DNAME=VALUE”,例如-DNUM="2+2",預處理器將把NUM替換成2+2。當宏是某個表達式的一部分時,用圓括號把宏括起來是個好主意,比如10 * (NUM)
宏通常都是未定義的除非用“-D”選項在命令行上指定,或在源文件中(或庫的頭文件)用#define定義。有些宏是由編譯器自動定義的----這些宏會用到由雙下划線(__)開始的保留的名字空間。預定義的宏的完整列表可以這樣得到,對某個空文件運行帶“-dM”選項的GNU預處理器cpp:
[root@iZ23i5mx5vxZ ~]# cpp -dM /dev/null #define __DBL_MIN_EXP__ (-1021) #define __FLT_MIN__ 1.17549435e-38F #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1 #define __FLT_EVAL_METHOD__ 0 #define __unix__ 1 #define __x86_64 1 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __GNUC_PATCHLEVEL__ 7 #define __DEC64_MAX_EXP__ 385 #define __SHRT_MAX__ 32767 #define __LDBL_MAX__ 1.18973149535723176502e+4932L #define __UINTMAX_TYPE__ long unsigned int #define __linux 1 #define __DEC32_EPSILON__ 1E-6DF #define __unix 1 #define __LDBL_MAX_EXP__ 16384 #define __linux__ 1 #define __SCHAR_MAX__ 127 #define __DBL_DIG__ 15 #define __SIZEOF_INT__ 4 #define __SIZEOF_POINTER__ 8 #define __USER_LABEL_PREFIX__ #define __STDC_HOSTED__ 1 #define __LDBL_HAS_INFINITY__ 1 #define __FLT_EPSILON__ 1.19209290e-7F #define __LDBL_MIN__ 3.36210314311209350626e-4932L #define __DEC32_MAX__ 9.999999E96DF #define __SIZEOF_LONG__ 8 #define __DECIMAL_DIG__ 21 #define __gnu_linux__ 1 #define __LDBL_HAS_QUIET_NAN__ 1 #define __GNUC__ 4 #define __MMX__ 1 #define __FLT_HAS_DENORM__ 1 #define __SIZEOF_LONG_DOUBLE__ 16 #define __BIGGEST_ALIGNMENT__ 16 #define __DBL_MAX__ 1.7976931348623157e+308 #define __DBL_HAS_INFINITY__ 1 #define __DEC32_MIN_EXP__ (-94) #define __LDBL_HAS_DENORM__ 1 #define __DEC128_MAX__ 9.999999999999999999999999999999999E6144DL #define __DEC32_MIN__ 1E-95DF #define __DBL_MAX_EXP__ 1024 #define __DEC128_EPSILON__ 1E-33DL #define __SSE2_MATH__ 1 #define __amd64 1 #define __LONG_LONG_MAX__ 9223372036854775807LL #define __SIZEOF_SIZE_T__ 8 #define __SIZEOF_WINT_T__ 4 #define __GCC_HAVE_DWARF2_CFI_ASM 1 #define __GXX_ABI_VERSION 1002 #define __FLT_MIN_EXP__ (-125) #define __DBL_MIN__ 2.2250738585072014e-308 #define __LP64__ 1 #define __DECIMAL_BID_FORMAT__ 1 #define __DEC128_MIN__ 1E-6143DL #define __REGISTER_PREFIX__ #define __DBL_HAS_DENORM__ 1 #define __NO_INLINE__ 1 #define __FLT_MANT_DIG__ 24 #define __VERSION__ "4.4.7 20120313 (Red Hat 4.4.7-17)" #define __DEC64_EPSILON__ 1E-15DD #define __DEC128_MIN_EXP__ (-6142) #define unix 1 #define __SIZE_TYPE__ long unsigned int #define __ELF__ 1 #define __FLT_RADIX__ 2 #define __LDBL_EPSILON__ 1.08420217248550443401e-19L #define __GNUC_RH_RELEASE__ 17 #define __SSE_MATH__ 1 #define __k8 1 #define __SIZEOF_PTRDIFF_T__ 8 #define __x86_64__ 1 #define __DEC32_SUBNORMAL_MIN__ 0.000001E-95DF #define __FLT_HAS_QUIET_NAN__ 1 #define __FLT_MAX_10_EXP__ 38 #define __LONG_MAX__ 9223372036854775807L #define __DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL #define __FLT_HAS_INFINITY__ 1 #define __DEC64_MAX__ 9.999999999999999E384DD #define __CHAR16_TYPE__ short unsigned int #define __DEC64_MANT_DIG__ 16 #define __DEC32_MAX_EXP__ 97 #define linux 1 #define __SSE2__ 1 #define __LDBL_MANT_DIG__ 64 #define __DBL_HAS_QUIET_NAN__ 1 #define __k8__ 1 #define __WCHAR_TYPE__ int #define __SIZEOF_FLOAT__ 4 #define __DEC64_MIN_EXP__ (-382) #define __FLT_DIG__ 6 #define __INT_MAX__ 2147483647 #define __amd64__ 1 #define __FLT_MAX_EXP__ 128 #define __DBL_MANT_DIG__ 53 #define __DEC64_MIN__ 1E-383DD #define __WINT_TYPE__ unsigned int #define __SIZEOF_SHORT__ 2 #define __SSE__ 1 #define __LDBL_MIN_EXP__ (-16381) #define __LDBL_MAX_10_EXP__ 4932 #define __DBL_EPSILON__ 2.2204460492503131e-16 #define _LP64 1 #define __SIZEOF_WCHAR_T__ 4 #define __DEC_EVAL_METHOD__ 2 #define __INTMAX_MAX__ 9223372036854775807L #define __FLT_DENORM_MIN__ 1.40129846e-45F #define __CHAR32_TYPE__ unsigned int #define __FLT_MAX__ 3.40282347e+38F #define __SIZEOF_DOUBLE__ 8 #define __FLT_MIN_10_EXP__ (-37) #define __INTMAX_TYPE__ long int #define __DEC128_MAX_EXP__ 6145 #define __GNUC_MINOR__ 4 #define __DEC32_MANT_DIG__ 7 #define __DBL_MAX_10_EXP__ 308 #define __LDBL_DENORM_MIN__ 3.64519953188247460253e-4951L #define __STDC__ 1 #define __PTRDIFF_TYPE__ long int #define __DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD #define __DEC128_MANT_DIG__ 34 #define __LDBL_MIN_10_EXP__ (-4931) #define __SIZEOF_LONG_LONG__ 8 #define __LDBL_DIG__ 18 #define __GNUC_GNU_INLINE__ 1
“-E”選項導致gcc只運行預處理器,顯示展開后的輸出后,沒有編譯預處理過的源代碼就退出。
“-g”調試選項來在對象文件和可執行文件中存儲另外的調試信息。這些調試信息可以使得在追蹤錯誤時能從特定的機器碼指令對應到源代碼文件中的行。它也可以使得該程序能被象gdb之類的調試器追蹤調試。調試器是通過把函數名和變量(和所有對它們的引用),以及它們相應的原代碼的行號存儲到對象文件和可執行文件的符號表中來工作的。
“-g”選項除了允許程序在調試器控制下運行以外,另一個有用的應用是找到程序崩潰的環境。
當一個程序異常退出時,操作系統會寫一個常規被稱為“core”的文件,它包括了程序在崩潰剎那間的內存狀態。結合由“-g”選項生成的符號表中的信息,程序員從core文件中可以查詢到程序在哪一行停止了,和在那時候的變量的值。這在軟件開發期間和部署以后都是很有用的----它允許當程序在“某處”崩潰時能對問題展開調查。例如:
Segmentation fault (core dumped)
只要顯示了報錯信息“core dumped”,操作系統就在當前目錄下生成了一個名為“core”的文件。該core文件包含程序在被終結時用到的內存頁面的完整備份。由於core文件可能很大並且可能快速地填滿系統中的可用磁盤空間,一些系統被配置成在默認情況下不寫core文件。在GNU Bash shell中,命令ulimit –c可以控制設定core文件的最大值。如果這個限定值是零,則不會生成core文件。輸入下面的命令可以顯示當前的限定值:
[root@iZ23i5mx5vxZ ~]# ulimit -a
core file size (blocks, -c) 0
ulimit -c unlimited 可設置允許的core文件大小。
GNU調試器gdb可以用下面的命令載入core文件:
$ gdb EXECUTABLE-FILE CORE-FILE
注意,原來的可執行文件和core文件都要提供,以備調試----沒有相應的可執行文件,只有core文件是不能調試的。
注:用gdb調試進程時最煩瑣的就是如何繼續跟進fork出的子進程。GDB 7.0支持調試子進程,參考https://blog.csdn.net/pbymw8iwm/article/details/7876797
例如:
$ gdb a.out core
調試器馬上打印出診斷信息和顯示程序崩潰處的代碼行(第13行):
$ gdb a.out core
Core was generated by ‘./a.out’.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x080483ed in a (p=0x0) at null.c:13
13 int y = *p;
(gdb)
“-OLEVEL”用來選擇哪一種優化級別,這里LEVEL是從1到3的數字。“-O0”或沒有“-O”選項(默認),在該優化級別,GCC不會實施任何優化。
“-O1”或“-O”這一級會打開那些不需要任何速度-空間折衷的最常見形式的優化。帶選項“-O1”編譯所花時間可能常常少於帶“-O0”編譯,這是由於在簡單優化后減少了需要處理的數據量。
“-O2”該選項除了“-O1”用到的那些優化以外,打開進一步優化。對要部署的程序而言,該選項通常是最佳選擇,因為在不增加可執行文件大小的情況下,它提供了最大的優化。它是各種GNU軟件發行包的默認優化級別。
就絕大部分目的而言,調試時用“-O0”,開發和部署時用“-O2”就足夠了。
在GCC下,可以組合使用優化與調試選項“-g”,而許多其他編譯器不支持這樣做。當程序出人意料地崩潰時,有點調試信息總比什么都沒有要好----所以推薦你在優化程序時加上“-g”選項,即為了開發,也為了部署。GNU發行的軟件包默認都打開了調試選項“-g”和優化選項“-O2”。
============================
GCC的C++前端用到很多同C編譯器gcc相同的選項。它也支持另外一些選項來控制C++才有的語言特性。在使用g++時,一個很自然的不同點是“-ansi”選項的要求是兼容於C++標准,而不是C標准。
要注意的是,C++對象文件必須用g++來鏈接,以便與適當的C++庫鏈接。試圖用C編譯器gcc來鏈接C++對象文件會導致由於找不到C++標准庫函數而報“未定義引用”的錯。
由GCC提供的C++標准庫“libstdc++”除了包含象排序等泛型算法外,還包含很多象列表和隊列等的泛型容器類。這些類原本是標准模板庫(STL)的一部分,是一個獨立的包,但現在已經被包括進C++標准庫中。
由g++創建的使用C++標准庫的可執行文件會被鏈接到共享庫“libstdc++”,該庫作為GCC的一部分而被默認安裝。它有幾個版本----如果你要發布使用C++標准庫的可執行文件,你需要確保接受方安裝了“libstdc++”的兼容版本,或者你索性用“-static”命令行選項來靜態鏈接你的程序。
g++中使用模板的推薦方法是遵循“包含編譯模型(inclusion compilation model)”,即把模板的定義放到頭文件中(所以,現在有很多的庫文件稱之為header-only library,見c++中的header-only library)。GCC的C++標准庫本身就使用該方法。用到模板的源文件通過“#include”指示符包括入模板所在的頭文件。
查看完整選項:gcc –v –help
GNU歸檔工具ar用於把多個對象文件組合成歸檔文件,也被稱作庫。歸檔文件是一種把相關各個對象文件打包在一起發行的簡便方法。
ar cr libhello.a hello_fn.o bye_fn.o
選項“cr”代表“create and replace”。
ar t libhello.a
hello_fn.o
bye_fn.o
選項“t”來列出已有庫中的對象文件。
注意,當發行一個庫時,該庫提供的公開的函數和變量相關的頭文件應該是可獲得的,以便最終用戶能include該頭文件以獲得正確的函數原型。
“-S”指導gcc把預處理過的C源代碼轉變成匯編語言,但不生成對象文件(obj文件)
在源代碼文件被編譯成對象文件或可執行文件以后,編譯時指定的編譯選項就不再那么容易知道了。file命令查看對象文件或可執行文件的內容來查看它的特性,比如象它是動態鏈接的還是靜態鏈接的。
[root@iZ23i5mx5vxZ bin]# file mysql
mysql: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
not stripped:可執行文件包含符號表(符號表可以用strip命令刪除)。
nm命令可以看到符號表,如:
[root@iZ23i5mx5vxZ bin]# nm mysql | more U ASN1_STRING_data@@libcrypto.so.10 U ASN1_STRING_length@@libcrypto.so.10 U BIO_free@@libcrypto.so.10 U BIO_new_bio_pair@@libcrypto.so.10 U BIO_new_mem_buf@@libcrypto.so.10 U BN_bin2bn@@libcrypto.so.10 U CRYPTO_cleanup_all_ex_data@@libcrypto.so.10 U CRYPTO_free@@libcrypto.so.10 U CRYPTO_malloc@@libcrypto.so.10 U CRYPTO_num_locks@@libcrypto.so.10 U CRYPTO_set_dynlock_create_callback@@libcrypto.so.10 U CRYPTO_set_dynlock_destroy_callback@@libcrypto.so.10 U CRYPTO_set_dynlock_lock_callback@@libcrypto.so.10 U CRYPTO_set_id_callback@@libcrypto.so.10 U CRYPTO_set_locking_callback@@libcrypto.so.10 000000000048fcc0 r CSWTCH.141 000000000048fc80 r CSWTCH.169 0000000000937040 d CZ_SORT_TABLE U DH_free@@libcrypto.so.10 U DH_new@@libcrypto.so.10 0000000000941898 V DW.ref.__gxx_personality_v0 U EC_KEY_free@@OPENSSL_1.0.1_EC U EC_KEY_new_by_curve_name@@OPENSSL_1.0.1_EC U ERR_clear_error@@libcrypto.so.10 U ERR_error_string_n@@libcrypto.so.10 U ERR_free_strings@@libcrypto.so.10 U ERR_get_error@@libcrypto.so.10 U ERR_get_error_line_data@@libcrypto.so.10 U ERR_remove_state@@libcrypto.so.10 U ERR_remove_thread_state@@libcrypto.so.10 U EVP_CIPHER_CTX_cleanup@@libcrypto.so.10 U EVP_CIPHER_CTX_init@@libcrypto.so.10 U EVP_CIPHER_CTX_set_padding@@libcrypto.so.10 U EVP_CIPHER_block_size@@libcrypto.so.10
nm命令的最常應用是檢查某個庫是否包含的特定函數的定義,通過查找第二列為“T”項的函數名即可。
ldd命令用於檢查可執行文件並顯示它需要的共享庫的列表。這些庫被稱為該可執行文件的共享庫依賴。如:
[root@iZ23i5mx5vxZ bin]# ldd mysql linux-vdso.so.1 => (0x00007fff61d61000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00000034cec00000) libreadline.so.6 => /lib64/libreadline.so.6 (0x000000340b600000) libtinfo.so.5 => /lib64/libtinfo.so.5 (0x000000340a600000) libz.so.1 => /lib64/libz.so.1 (0x00000034ce400000) librt.so.1 => /lib64/librt.so.1 (0x0000003772400000) libssl.so.10 => /usr/lib64/libssl.so.10 (0x00000034d0400000) libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00000034cfc00000) libdl.so.2 => /lib64/libdl.so.2 (0x00000034cf000000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003af6800000) libm.so.6 => /lib64/libm.so.6 (0x00000034cf800000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003af6400000) libc.so.6 => /lib64/libc.so.6 (0x00000034ce800000) /lib64/ld-linux-x86-64.so.2 (0x00000034ce000000) libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x0000003420400000) libkrb5.so.3 => /lib64/libkrb5.so.3 (0x0000003421000000) libcom_err.so.2 => /lib64/libcom_err.so.2 (0x000000341fc00000) libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x0000003421400000) libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x0000003420c00000) libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x0000003420000000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00000034d0800000) libselinux.so.1 => /lib64/libselinux.so.1 (0x000000341f800000)
ldd命令也能夠用於檢查共享庫本身,可以跟蹤共享庫依賴鏈。
gcc默認全部導出符號表,vc默認全部不導出,所以在vc中調用dll時會涉及到導出導入符號表的管理,可參考https://msdn.microsoft.com/en-us/library/9h658af8(v=vs.110).aspx、https://msdn.microsoft.com/en-us/library/8fskxacy(v=vs.110).aspx。
gcc關於如何查看清除目標文件中的符號表,可參考http://blog.csdn.net/swedenfeng/article/details/53417085。