目錄
1. 獲取 GCC 編譯器預定義的宏
2. 列出依賴的頭文件
3. 保存預處理結果到文件(展開define, 展開include header)
4. 寫回調跟蹤記錄函數運行?-finstrument-functions
5.?-fdump-rtl-expand 畫函數調用關系圖
GCC,全稱GNU Compiler Collection,是一套功能強大的開源編譯器,支持多種編程語言,如C、C++、Fortran、Objective-C、Ada等。GCC的靈活性和可移植性使得它在各種系統和項目中都有廣泛的應用。然而,要充分利用GCC的全部潛力,需要熟悉一些高級特性和使用技巧。在本文中,我們將探討一些GCC的進階用法,幫助開發者優化代碼、提高編譯效率并減少潛在錯誤。
1. 獲取 GCC 編譯器預定義的宏
-E
: 只運行預處理器。這意味著編譯器將只處理源文件中的預處理器指令(例如?#include
、#define
?等),然后輸出預處理后的代碼。-dM
: 輸出所有的宏定義。當與?-E
?一起使用時,這將輸出編譯器預定義的所有宏。
]# gcc -E -dM -</dev/null
#define __DBL_MIN_EXP__ (-1021)
#define __FLT32X_MAX_EXP__ 1024
#define __UINT_LEAST16_MAX__ 0xffff
#define __ATOMIC_ACQUIRE 2
#define __FLT128_MAX_10_EXP__ 4932
#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F
#define __GCC_IEC_559_COMPLEX 2
#define __UINT_LEAST8_TYPE__ unsigned char
#define __SIZEOF_FLOAT80__ 16
#define __INTMAX_C(c) c ## L
#define __CHAR_BIT__ 8
#define __UINT8_MAX__ 0xff
#define __WINT_MAX__ 0xffffffffU
#define __FLT32_MIN_EXP__ (-125)
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __SIZE_MAX__ 0xffffffffffffffffUL
#define __WCHAR_MAX__ 0x7fffffff
#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__ ((double)4.94065645841246544176568792868221372e-324L)
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
#define __GCC_IEC_559 2
2. 列出依賴的頭文件
用于生成文件的依賴關系。這個選項對于大型項目特別有用,因為它可以幫助你跟蹤哪些頭文件被哪個源文件包含,以及當頭文件更改時哪些源文件需要重新編譯。
當你使用?gcc -M
?選項時,GCC 會輸出一個規則集,這個規則集描述了源文件和目標文件之間的依賴關系。輸出的規則可以直接被?make
?工具使用,從而自動化構建過程。
$ gcc -M sizeofstruct.csizeofstruct.o: sizeofstruct.c /usr/include/stdc-predef.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.5/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.5/include/stdarg.h \/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h
3. 保存預處理結果到文件(展開define, 展開include header)
# cat test_e.c
#include<unistd.h>int main(int argc, char** argv){printf("Hello world");return 0;
}# gcc -E test_e.c -o test_e.i[root@jun cprogram]# more test_e.i
# 1 "test_e.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test_e.c"
# 1 "/usr/include/unistd.h" 1 3 4
# 25 "/usr/include/unistd.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 428 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 442 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 443 "/usr/include/sys/cdefs.h" 2 3 4
# 1 "/usr/include/bits/long-double.h" 1 3 4
# 444 "/usr/include/sys/cdefs.h" 2 3 4
# 429 "/usr/include/features.h" 2 3 4
# 452 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 453 "/usr/include/features.h" 2 3 4
# 26 "/usr/include/unistd.h" 2 3 4# 202 "/usr/include/unistd.h" 3 4
# 1 "/usr/include/bits/posix_opt.h" 1 3 4
# 203 "/usr/include/unistd.h" 2 3 4# 1 "/usr/include/bits/environments.h" 1 3 4
...# wc test_e.i1416 2357 24475 test_e.i
4. 寫回調跟蹤記錄函數運行?-finstrument-functions
具體見老外寫的一篇blog
我們組曾經用此跟蹤過一個很棘手的問題。細節先不談。
5.?-fdump-rtl-expand 畫函數調用關系圖
用于在編譯過程中輸出 RTL(Register Transfer Language)擴展階段的中間表示。
當使用這個選項時,GCC 會在編譯過程中生成一個包含 RTL 擴展階段信息的文本文件。這個文件以源文件的擴展名為基礎,添加了?.rtl-expand
?后綴。例如,如果你正在編譯名為?foo.c
?的源文件,生成的 RTL 擴展文件將會是?foo.c.rtl-expand
。
RTL 是 GCC 內部表示的一種中間語言,用于在編譯過程中的不同階段之間傳遞信息。RTL 擴展階段是 RTL 轉換過程的一部分,其中將高級 RTL 指令轉換為更低級別的 RTL 指令,以便后續的機器代碼生成階段。
我曾經用過此編譯選項來結合Egypt來畫函數調用關系圖,直接引用egypt給的一個例子