學習交流加程序員
- 我的qq: 1126137994
- 我的微信: liu1126137994
- 學習交流資源分享qq羣: 962535112
GCC (GNU Compiler Collection)編程
gcc 單指GCC中的C語言編譯器ubuntu
想了解更多更詳細的關於編譯連接深層次內容,請閱讀書籍《CSAPP》第7章與《程序員的自我修養》,由於這裏個人學習記錄只記錄結果與經常使用的幾個編譯方法。bash
咱們先來看一個簡單的程序:微信
test.c源程序:ide
#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 TEST
printf("TEST = %s\n", TEST);
#endif
return;
}
複製代碼
在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其實內部包含了預處理器,編譯器,彙編器,連接器這四部分。
這四部分這裏只是來簡單介紹一下(網上一大堆,本文側重點不在此):
本文的重點來了,上述的內容過於簡單,而本節的內容雖然不難,可是並不被大多數人所瞭解,因此是本文的重點學習記錄。
下面將要學習的gcc選項,在工做中具備很強的實用性。
gcc -E file.c -o file.i
複製代碼
實用上述編譯選項 -E 能夠獲得預處理後的文件,有時候咱們在程序中定義的宏可能有錯誤,而這種錯誤又很難找,此時若是能得預處理後的文件,就能夠方便定位錯誤。
寫彙編程序很難,可是若是先寫成C語言,再將這個C語言轉化成彙編語言,就會很簡單。gcc編譯工具中,-S選項,能夠達到這個目的。好比如下程序: foo.c程序:
#include <stdio.h>
void foo(){
printf("This is foo().\n");
}
複製代碼
咱們使用以下命令進行編譯:
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 %ebp
movl %esp, %ebp
subl $24, %esp
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
leave
ret
.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語言實現方面的細節,也有所幫助。
gcc -v file.c
複製代碼
獲取file.c使用的系統頭文件的位置
若是咱們想要知道程序中各個符號的內存佈局的信息,可使用以下命令:
gcc -Wl,-Map=file.map file.c -o file
複製代碼
有時候程序中須要的某一個常量會依賴工做環境的不一樣而改變,這個時候,咱們能夠將這個常量定義爲宏,可是這樣,咱們仍是須要每次都在源程序中將宏的值改變,這也很麻煩,此時就能夠利用編譯選項 -D,在編譯的命令行進行宏定義。
還有就是程序中或許會存在下屬這樣的代碼: 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;
}
複製代碼
test.h頭文件:
#include <stdio.h>
void func() {
#ifdef TEST
printf("TEST = %s\n", TEST);
#endif
return;
}
複製代碼
在頭文件中,有一處定義 # 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 = 0x80483e1
複製代碼
大多數人應該知道make,若是不知道也沒有關係。 在makefile中,make須要經過依賴關係來決定,每次構建時哪些文件須要從新編譯。使用gcc的-M選項,能夠獲得make所須要的源文件的依賴關係。-MM選項可讓gcc生成不包含系統文件的依賴關係。
好比有以下源文件: main.c源文件(main.h與foo.c的內容是什麼都行)
#include <stdio.h>
#include "main.h"
#include "foo.c"
int main(){
printf("Hello world!\n");
return 0;
}
複製代碼
對其進行以下編譯
gcc -M main.c
複製代碼
將獲得以下輸出:
能夠看到,這句是make所須要的main.c的依賴關係。
若是使用以下命令的話:
gcc -MM main.c
複製代碼
將獲得以下輸出:
結果顯而易見!!!當一個可執行程序的生成,須要使用其餘庫時,須要在連接時加以指定。這就須要用到gcc 的-l與-L選項。
假設一個程序叫作main.c,它編譯成可執行程序不光須要系統的標準庫,還須要一個庫:libfoo.a 且這個libfoo.a與main.c在同一個目錄,那麼在編譯main.c時,須要如下命令:
gcc -o main -L. main.c -lfoo
複製代碼
注意:
更加詳細的內容參考《程序員的自我修養》
今天學習了gcc的簡單概念,與gcc的經常使用的參數選項。