一、 C語言編譯過程
C語言的編譯過程可分為四個階段:
1、預處理(Preprocessing)
對源程序中的偽指令(即以#開頭的指令)和特殊符號進行處理的過程。
偽指令包括:1)宏定義指令;
2)條件編譯指令;
3)頭文件包含指令;
2、編譯(Compilation)
編譯就是將源程序轉換為計算機可以執行的二進制代碼。
說明:
在Linux下,目標文件的缺省后綴為.o
編譯程序將通過詞法分析和語法分析,將其翻譯成為等價的匯編代碼。
在使用gcc進行編譯時,缺省情況下,不輸出這個匯編代碼的文件。如果需要,可以在編譯時指定-S選項。這樣,就會輸出同名的匯編語言文件。
3、匯編(Assembly)
匯編的過程實際上是將匯編語言代碼翻譯成機器語言的過程。
產生一個擴展名為.o的目標文件。
4、鏈接(Linking)
目標代碼不能直接執行,要想將目標代碼變成可執行程序,還需要進行鏈接操作。才會生成真正可以執行的可執行程序。
鏈接操作最重要的步驟就是將函數庫中相應的代碼組合到目標文件中。
二、文件名后綴
gcc可以針對支持不同的源程序文件進行不同的處理,文件格式以文件的后綴來識別。
文件名后綴 |
文件類型 |
.c |
C源文件 |
.C .cpp .cc .c++ .cxx |
C++源文件 |
.h |
頭文件 |
.i |
預處理后的C源文件 |
.s |
匯編程序文件 |
.o |
目標文件 |
.a |
靜態鏈接庫 |
.so |
動態鏈接庫 |
三、 gcc編譯器簡介
gcc(GNU Compiler Collection)
在Linux平台上最常用的C語言編譯系統是gcc,它是GNU項目中符合ANSI C標准的編譯系統。
gcc的使用格式:
gcc [options][filenames]
說明:當不用任何選項時,gcc將會生成一個名為a.out的可執行文件。
例子:在linux上編譯一個c程序(文件名為hello.c ;執行gcc hello.c)。
#include <stdio.h>
int main()
{
printf("hello world.\n");
return 0;
}
運行編譯好的可執行c文件命令是./a.out
四、gcc編譯器的工作過程
1、預處理(Preprocessing)
2、編譯(Compilation & Assembly)
源代碼轉換為匯編語言(在編譯時選擇-S選項,可以看到生成的匯編代碼.s文件)
匯編代碼(.s)轉換為目標代碼(.o)
3、鏈接(Linking)
將目標代碼與各庫函數進行鏈接並重定位,生成可執行程序。
五、gcc命令行選項
1、預處理選項
選項 |
說明 |
-D name |
定義一個宏name,並可以指定值 |
-I dir |
指定頭文件的路徑dir。先在指定的路徑中搜索要包含的頭文件,若找不到,則在標准路徑(/usr/include,/usr/lib及當前工作目錄)上搜索。 |
-E |
只對文件進行預處理,不進行編譯、匯編、鏈接,生成的結果送標准輸出 即:只運行C預編譯器 |
-o file |
將輸出寫到指定的文件file中 即:產生目標(.i 、.s 、 .o 、可執行文件等) |
例子:使用 -I選項包含保存在非標准位置中的頭文件。
# gcc -I/usr/openwin/include file.c
例子:使用-D選項定義宏,其作用等價於在源文件中使用宏定義指令。
main()
{
printf("display -D variable %s\n",DOPTION);
printf("hello,everybody!!\n");
}
# gcc -D DOPTION='"testing -D"' hello.c
2、編譯程序選項
選項 |
說明 |
-o file1 file2 |
將文件file2編譯成可執行文件file1。 如果未使用該選項,則可執行文件放在a.out中 |
-S |
只進行編譯,不進行匯編,生成匯編代碼文件擴展名為.s 即:告訴編譯器產生匯編語言文件后停止編譯 |
-c |
只把源文件編譯成目標代碼.o,不進行匯編、鏈接。 用於實現對源文件的分別編譯 |
-g |
在目標代碼中加入供調試程序gdb使用的附加信息 |
-v |
顯示gcc版本 |
-Wall |
顯示警告信息 |
例子:在gcc中使用-W控制警告信息。
# gcc -Wall -o hello1 hello1.c
例子:使用gcc的-g選項來產生調試符號,
# gcc -g -o test1 test1.c
例子:多文件的編譯。
//meng1.c
#include <stdio.h>
main()
{
int r;
printf("enter an integer,please!\n");
scanf("%d",&r);
square(r);
return 0;
}
//meng2.c
#include <stdio.h>
int square(int x)
{
printf("The square=%d\n",x*x);
return (x*x);
}
編譯方法一:
# gcc -c meng1.c
# gcc -c meng2.c
# gcc meng1.o meng2.o -o meng12
編譯方法二:
# gcc -o meng13 meng1.c meng2.c
說明:
方法二不產生中間目標文件,直接生成一個可執行文件,因而,程序內容稍有改動,就要重新編譯全部程序。
3、優化程序選項
優化是編譯器的一部分,它可以檢查和組合編譯器生成的代碼,指出未達到最優的部分,並重新生成它們,從而使用戶編寫的程序更加完美且節省空間。
在gcc編譯器選項中,使用-O選項對代碼進行優化。
優化級別分3級,由高到低分別為:-O3、-O2、-O1,
優化程序選項
選項 |
說明 |
-O1(-O) |
對編譯出的代碼進行優化 |
-O2 |
進行比-O高一級的優化 |
-O3 |
產生更高級別的優化 |
說明:
-O1(或-O)、-O2、-O3分別代表優化級別,數字越高,代表gcc的優化級別越高,高的優化級別代表着程序將運行的更快。
優化級別越高則程序量越大。
直接優化程序本身,性能的提高的變化更加明顯。
4、連接程序選項
庫:是一組預先編譯好的函數集合。
說明:
標准庫文件一般存儲在/lib和/usr/lib目錄中。
所有的庫名都以lib開頭。例如:libc.so(標准C語言函數庫)、libm.so(數學運算函數庫)
以.a結尾的是靜態庫;以.so結尾的庫是動態庫。
使用ar工具將目標文件收集起來,放到一個歸檔文件中。
連接程序選項
選項 |
說明 |
-L dir |
將dir所指出的目錄加到“函數庫搜索列表”中 |
-llib |
鏈接lib庫 |
-I name |
連接時,加載名字為name的函數庫。該庫位於系統預設的目錄或者由-L選項確定的目錄下。 實際的庫名是libname(后綴為.a或.so) |
說明:
鏈接過程通常的形式如下:
gcc -o file file.o -L dirname -lxxx
-L:指定了鏈接時用到的庫文件所在的目錄。
-lxxx:指示鏈接的庫函數名為libxxx.a
例子:編譯產生可執行文件hello,搜索數學庫以解決問題。
# gcc -o hello hello.c /usr/lib/libm.a
或者
# gcc -o hello hello.c -lm
例子:創建一個小型庫
包含兩個函數pro1、pro2,然后在示例程序中調用其中一個函數。
/* pro1.c */
#include <stdio.h>
void pro1(int arg)
{
printf("hello:%d\n", arg);
}
/* pro2.c */
#include <stdio.h>
void pro2(char *arg)
{
printf("welcome to:%s", arg);
}
/* lib.h */
void pro1(int);
void pro2(char *);
/* program.c */
#include "lib.h"
int main()
{
pro2("Linux world.");
exit(0);
}