一、什麼是GCC
GCC(the GNU Compiler Collection)GCC是GNU編譯器集合
它包括了C/C++、Objective-C、Fortran、Ada和Go語言的前端和對應的庫(libstdc++)GCC初衷是爲完全免費的GNU系統編寫的編譯器。
二、程序到計算機執行
一個C程序從編輯到執行需要經過以下過程:
編寫並保存程序(.c)->預處理(.c)成文件(.i)->彙編(.i)文件成(.s)->編譯(.s)成(.o)->鏈接(.o/.so )成可執行->加載可執行文件並進行庫的動態鏈接->內存中的程序->CPU執行
二、GCC編譯過程
GCC就是完成從程序到可執行文件的編譯套件。其過程分爲四個部分:預處理、編譯、彙編和鏈接。
假如我編輯並保存瞭如下程序
//gcctest.cpp
#include <iostream>
using namespace std;
#define AAA 33333
int main()
{
double a=3.14;//ordinary variable
double &r=a;//ordinary referenece
cout<<r<<endl;//read ok
r=41.3;//write ok
cout<<r<<endl;
cout<<AAA<<endl;
double b=3.14;
const double &rr =b;//read only, restrict its own right
cout<<rr<<endl;//read ok
//rr=41.3;//write not ok
//cout<<rr<<endl;
const double c=3.14;
// double &rrr=c;//reference trying to obtain read and write right failed
// cout<<rrr<<endl;
// rrr=41.3;
// cout<<rrr<<endl;
const double d=3.14;
const double &rrrr=d;
cout<<rrrr<<endl;
//rrrr=41.3;
//cout<<rrrr<<endl;
}
2.1 預處理 -E(Pre-pocessing)xxx.c->xxx.i
預處理會做三件事情,爲編譯做準備:
- 宏變量替換
- 頭文件展開
- 註釋去除
命令格式:gcc -E xxx.c
g++ gcctest.cpp -o app.i
這個-o
其實指定生成文件名字標識(不指定默認生成a.out
)
這一階段生成的文件(xxx.i),還是可以很容易閱讀:
//...頭文件展開部分
using namespace std;
int main()
{
double a=3.14;
double &r=a;
cout<<r<<endl;
r=41.3;
cout<<r<<endl;
cout<<33333<<endl;
double b=3.14;
const double &rr =b;
cout<<rr<<endl;
const double c=3.14;
const double d=3.14;
const double &rrrr=d;
cout<<rrrr<<endl;
}
2.2 編譯 -S(Compiling) xxx.i->xxx.s
編譯過後的文件稱爲彙編文件。命令格式:g++ xxx.i
,這裏對上一部分的.i文件進行了處理:
g++ app.i -S -o app.s
仍然是個普通的文本文件,不過語言是彙編:
.file "class0919.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.text
.globl main
.type main, @function
main:
.LFB1493:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $80, %rsp
//... 省略了一大段彙編語言
.LC1:
.long 1717986918
.long 1078240870
.hidden __dso_handle
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
2.3 彙編 -c(Assembling) xxx.s->xxx.o
彙編文件彙編後的文件類型是一個二進制文件,編譯過程中叫做目標文件,基本上人是沒法讀懂了:
亂碼
2.4 鏈接(Linking) xxx.o->xxx
對目標文件的集合稱爲可執行文件。命令g++ xxx1.o xxx2.o
g++ -o eee
內容一樣是亂碼,不過:
eee: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=06ad229f773d4211a1a47719e43e93cc2886c5c3, not stripped
三、GCC其他選項
GCC不僅具有編譯成可執行的功能,還具有添加有用信息(-g)和打印信息(調試),甚至還可以調節優化等級(-On)。
option | meaning |
---|---|
-o | 指定生成的文件名 |
-E | 僅執行編譯預處理 |
-S | 將C代碼轉換爲彙編代碼 |
-wall | 顯示警告信息 |
-c | 僅執行編譯操作,不進行連接操作 |
-g | 生成調試信息,不加選項不能進行gdb調試 |
查看GCC版本:
gcc --version
gcc -v
四、常見應用
3.1 無選項編譯
無選項編譯直接生成名爲a.out
的可執行文件
gcc test.c
將test.c預處理、彙編、編譯並鏈接成可執行文件a.out
。
3.2 選項 -o
gcc test.c -o test
和無選項編譯一樣,只不過生成的可執行名字變成了test
3.3 選項 -E
僅預處理:
gcc -E test.c -o test.i
將test.c預處理輸出test.i文件。
3.4 選項 -c
僅編譯:
gcc -c test.s
將彙編輸出文件test.s編譯輸出test.o文件。
3.5 選項 -S
僅彙編:
gcc -S test.i
將預處理輸出文件test.i彙編成test.s文件。
3.6 選項-O
優化選項:
gcc -O1 test.c -o test
使用編譯優化級別1編譯程序。級別爲1~3,級別越大優化效果越好,但編譯時間越長
3.8 多個文件一起編譯
第一次生成可以使用:
gcc testfun.c test.c -o test
一起編譯+鏈接生成test可執行
僅對部分源文件進行了修改,使用-o選項更快:
gcc -c testfun.c #將testfun.c編譯成testfun.o
gcc -c test.c #將test.c編譯成test.o
gcc -o testfun.o test.o -o test #將testfun.o和test.o鏈接成test