【软件开发底层知识修炼】五 gcc-C语言编译器
學習交流加
- 個人qq:
1126137994 - 個人微信:
liu1126137994 - 學習交流資源分享qq群:
962535112
文章目錄
- 1、GCC與gcc
- 2、gcc的幕后工作
- 3、實用的gcc選項
- 3.1、預處理選項-解決宏錯誤
- 3.2、-S參數-輔助編寫匯編程序的好方法
- 3.3、獲取系統頭文件路徑
- 3.4、產生映射文件
- 3.5、通過選項定義宏
- 3.6、生成依賴關系
- 3.7、指定鏈接庫
- 4、總結
前面的四篇文章終于把處理器系列學完了(點擊查看上一篇文章:高速緩存與TLB)。收獲很大!!! 接下來就該學習底層軟件部分知識。今天學習gcc的基本概念與簡單用法。
1、GCC與gcc
-
GCC (GNU Compiler Collection)
- GNU 編譯器集合,包含眾多語言的編譯器,包括C,C++,Java等等
- GCC多用于嵌入式操作系統的編譯,如Linux,VxWorks,Android等等
-
gcc 單指GCC中的C語言編譯器
- gcc 多用于內核開發以及少數應用程序開發
2、gcc的幕后工作
想了解更多更詳細的關于編譯鏈接深層次內容,請閱讀書籍《CSAPP》第7章與《程序員的自我修養》,因為這里我的學習記錄只記錄結果與常用的幾個編譯方法。
我們先來看一個簡單的程序:
test.c源程序:
#include <stdio.h> #include "func.h"int g_global = 0; int g_test = 1;int main(int argc, char *argv[]) {func();printf("&g_global = %p\n", &g_global);printf("&g_test = %p\n", &g_test);printf("&func = %p\n", &func);printf("&main = %p\n", &main);return 0; }func.h頭文件:
#include <stdio.h>void func() { #ifdef TESTprintf("TEST = %s\n", TEST); #endifreturn; }在Linux下使用gcc進行編譯:
gcc test.c -o test然后運行:
./test結果如下:
&g_global = 0x804a020 &g_test = 0x804a014 &func = 0x80483c4 &main = 0x80483c9很明顯,上述程序很簡單,大一的新生都知道為什么。但是今天我們不是學習這個程序的,而是想要了解,運行 gcc test.c -o test 這個命令后,是如何一步一步生成可執行文件test的。
實際上,上述C程序從源文件到二進制可執行文件,有以下四個步驟:
大概編譯一個源程序為二進制文件的過程如下圖所示:
當然,上面沒有列出鏈接器,在生成file.o后,還需要將file.o與系統的庫文件進行鏈接,生成最終的可執行文件。
從而,我們就知道了,gcc其實內部包含了預處理器,編譯器,匯編器,鏈接器這四部分。
這四部分這里只是來簡單介紹一下(網上一大堆,本文側重點不在此):
- 預處理器:預處理,將源程序的宏定義與帶‘#’的部分展開
- 編譯器:將預處理后得到的文件進行第一次編譯得到對應的匯編源程序
- 匯編器:將第二步得到的匯編源程序進行第二次編譯即匯編,得到二進制文件(可重定位文件)
- 鏈接器:將可重定位文件與相應的庫進行鏈接生成最終的可執行文件
3、實用的gcc選項
本文的重點來了,上述的內容過于簡單,而本節的內容雖然不難,但是并不被大多數人所了解,所以是本文的重點學習記錄。
下面將要學習的gcc選項,在工作中具有很強的實用性。
3.1、預處理選項-解決宏錯誤
gcc -E file.c -o file.i實用上述編譯選項 -E 可以得到預處理后的文件,有時候我們在程序中定義的宏可能有錯誤,而這種錯誤又很難找,此時如果能得預處理后的文件,就可以方便定位錯誤。
3.2、-S參數-輔助編寫匯編程序的好方法
寫匯編程序很難,但是如果先寫成C語言,再將這個C語言轉化成匯編語言,就會很簡單。gcc編譯工具中,-S選項,可以達到這個目的。比如以下程序:
foo.c程序:
我們使用如下命令進行編譯:
gcc -S -O2 foo.c -o foo.s將會生成一個foo.c相同作用的匯編程序foo.s,如下:
.file "foo.c".section .rodata.str1.1,"aMS",@progbits,1 .LC0:.string "This is foo().\n".text.p2align 4,,15 .globl foo.type foo, @function foo:pushl %ebpmovl %esp, %ebpsubl $24, %espmovl $.LC0, 4(%esp)movl $1, (%esp)call __printf_chkleaveret.size foo, .-foo.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5".section .note.GNU-stack,"",@progbits使用-S 參數時,我們可以根據需要使用-O優化選項。從foo.s的內容可以看出,“This is foo().\n” 這個字符串是放在.rodata段的。看來獲取C程序對應的匯編代碼,對C語言實現方面的細節,也有所幫助。
3.3、獲取系統頭文件路徑
gcc -v file.c獲取file.c使用的系統頭文件的位置
3.4、產生映射文件
如果我們想要知道程序中各個符號的內存布局的信息,可以使用如下命令:
gcc -Wl,-Map=file.map file.c -o file3.5、通過選項定義宏
有時候程序中需要的某一個常量會依賴工作環境的不同而改變,這個時候,我們可以將這個常量定義為宏,但是這樣,我們還是需要每次都在源程序中將宏的值改變,這也很麻煩,此時就可以利用編譯選項 -D,在編譯的命令行進行宏定義。
還有就是程序中或許會存在下屬這樣的代碼:
test.c程序:
test.h頭文件:
#include <stdio.h>void func() { #ifdef TESTprintf("TEST = %s\n", TEST); #endifreturn; }在頭文件中,有一處定義 # ifdef TEST …
很明顯,上面的兩個文件,都沒有定義這個TEST,所以程序運行結果如下:
&g_global = 0x804a020&g_test = 0x804a014&func = 0x80483c4&main = 0x80483c9但是可能在某個場合,又必須要使用TEST定義,那么此時,我們肯定不愿意在程序中改來改去,此時就利用編譯器的 -D選項,來定義這個TEST。如下編譯命令:
gcc -D'TEST="test" ' test.c -o test運行程序后,結果如下:
TEST = test &g_global = 0x804a020 &g_test = 0x804a014 &func = 0x80483c4 &main = 0x80483e13.6、生成依賴關系
大多數人應該知道make,如果不知道也沒有關系。
在makefile中,make需要通過依賴關系來決定,每次構建時哪些文件需要重新編譯。使用gcc的-M選項,可以得到make所需要的源文件的依賴關系。-MM選項可以讓gcc生成不包含系統文件的依賴關系。
比如有如下源文件:
main.c源文件(main.h與foo.c的內容是什么都行)
對其進行如下編譯
gcc -M main.c將得到如下輸出:
可以看到,這句是make所需要的main.c的依賴關系。
如果使用如下命令的話:
gcc -MM main.c將得到如下輸出:
結果顯而易見!!!
3.7、指定鏈接庫
當一個可執行程序的生成,需要使用其他庫時,需要在鏈接時加以指定。這就需要用到gcc 的-l與-L選項。
假設一個程序叫做main.c,它編譯成可執行程序不光需要系統的標準庫,還需要一個庫:libfoo.a 且這個libfoo.a與main.c在同一個目錄,那么在編譯main.c時,需要以下命令:
gcc -o main -L. main.c -lfoo注意:
- -L告訴gcc編譯器,當前可以從哪個目錄查找庫文件,此處-L后面跟了一個**點‘.’**表示當前目錄。
- -l選項,告訴編譯器需要連接的庫名。這里并沒有寫“lib”前綴和“.a后綴”。-lfoo就是代表指定libfoo.a庫參與鏈接。
更加詳細的內容參考《程序員的自我修養》
4、總結
今天學習了gcc的簡單概念,與gcc的常用的參數選項。
本文章參考狄泰軟件學院相關課程與《專業嵌入式軟件》第4章的內容內容
想學習的可以加狄泰軟件學院群,
群聊號碼:199546072
學習探討加個人:
qq:1126137994
微信:liu1126137994
總結
以上是生活随笔為你收集整理的【软件开发底层知识修炼】五 gcc-C语言编译器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue怎么合并两个视频_【软件分享】视频
- 下一篇: amazons3 检查连接是否_钢筋机械