一、首先,先對Linux下C語言開發的流程有個大體的瞭解。簡單的說,就是:
1、編寫C程式原始碼*.c 2、預先處理(Pre-Processing) 3、編譯(Compiling) 4、彙編(Assembling)-->產生目標代碼*.o 5、連結(Linking)-->產生可執行檔 6、調試 其中,第一步工作用編輯器來實現,用Emacs、Vi(m)都可以。我現在習慣用Vim。在RedHat9.0中,Vim版本為6.1,系統採用alias用vi作為vim的別名,因此在shell提示符下輸入vi實際上使用的編輯器vim(Vi IMproved)。你可以使用which查看一下:[armlinux@lqm program]$ which vi
alias vi='vim'
/usr/bin/vim 除去第6步的工作由GDB之類的調試器來完成,中間的四步工作都是由GCC來完成的。 二、下面大體瞭解一下GCC。---------------------------------------標準
ANSI C:這一標準是 ANSI(美國國家標準局)於 1989 年制定的 C 語言標準。後來被 ISO(國際標準組織)接受為標準,因此也稱為 ISO C。
ANSI C 的目標是為各種作業系統上的 C 程式提供可移植性保證,而不僅僅限於 UNIX。 該標準不僅定義了 C 程式設計語言的語發和語義,而且還定義了一個標準庫。這個庫可以根據 標頭檔劃分為 15 個部分,其中包括:字元類型 (<ctype.h>)、錯誤碼 (<errno.h>)、 浮點常數 (<float.h>)、數學常數 (<math.h>)、標準定義 (<stddef.h>)、 標準 I/O (<stdio.h>)、工具函數 (<stdlib.h>)、字串操作 (<string.h>)、 時間和日期 (<time.h>)、可變參數表 (<stdarg.h>)、訊號 (<signal.h>)、 非局部跳轉 (<setjmp.h>)、本地資訊 (<local.h>)、程式斷言 (<assert.h>) 等等。
POSIX:該標準最初由 IEEE 開發的標準族,部分已經被 ISO 接受為國際標準。該標準的具體內容 見 1.1.3。POSIX.1 和 POSIX.2 分別定義了 POSIX 相容作業系統的 C 語言系統介面 以及 shell 和工具標準。這兩個標準是通常提到的標準。
SVID:System V 的介面描述。System V 介面描述(SVID)是描述 AT&&;T Unix System V 操作 系統的文檔,是對 POSIX 標準的擴充超集。
XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd.出版), 是比 POSIX 更為一般的標準。X/Open 擁有 Unix 的著作權,而 XPG 則指定成為 Unix 作業系統必須滿足的要求。--------------------------------------- GCC是GNU CC的簡稱,它是符合上述ANSI C標準的編譯系統,能夠編譯C、C++、Object C等語言編寫的程式。GCC還是一個交叉平台編譯器,能夠在當前CPU平台為多種不同架構的硬體平台開發軟體,因此適合嵌入式領域的開發編譯。GCC所支援的尾碼名的解釋.c C原始程式.C/.cc/.cxx C++原始程式.m Object C原始程式.i 已經過預先處理的C原始程式.ii 已經過預先處理的C++原始程式.s/.S 組合語言原始程式.h 預先處理檔案(標頭檔).o 目標檔案.a/.so 編譯後的庫檔案 三、詳解GCC編譯流程 使用vi編寫源檔案hello.c。#include <stdio.h>int main()
{
int i;
for(i=1;i<9;i++)
printf("Hello World %d times!/n",i);
return 0;
} 1、預先處理階段 該階段的作用是把預先處理檔案,也就是標頭檔編譯進來。在此例中,就是要把stdio.h編譯進來。可使用-E選項查看,作用是讓gcc在預先處理結束後停止編譯過程。[armlinux@lqm program]$ gcc -E hello.c -o hello.i[armlinux@lqm program]$ cat hello.i | less# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3
# 28 "/usr/include/stdio.h" 3
# 1 "/usr/include/features.h" 1 3
# 291 "/usr/include/features.h" 3
# 1 "/usr/include/sys/cdefs.h" 1 3
# 292 "/usr/include/features.h" 2 3
# 314 "/usr/include/features.h" 3
# 1 "/usr/include/gnu/stubs.h" 1 3
# 315 "/usr/include/features.h" 2 3
# 29 "/usr/include/stdio.h" 2 3...extern void funlockfile (FILE *__stream) ;
# 679 "/usr/include/stdio.h" 3# 2 "hello.c" 2int main()
{
int i;
for(i=1;i<9;i++)
printf("Hello World %d times!/n",i);
return 0;
} 由此可見,stdio.h的內容已經插入到hello.c中,即GCC完成了預先處理過程。 2、編譯階段 GCC的工作是首先檢查代碼的規範性、是否有語法錯誤,以確定代碼實際要做的工作。檢查無誤後,將之翻譯為組合語言。可用-S來查看,即只編譯而不進入彙編階段。[armlinux@lqm program]$ gcc -S hello.i -o hello.s[armlinux@lqm program]$ cat hello.s .file "hello.c"
.section .rodata
.LC0:
.string "Hello World %d times!/n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movl $1, -4(%ebp)
.L2:
cmpl $8, -4(%ebp)
jle .L5
jmp .L3
.L5:
subl $8, %esp
pushl -4(%ebp)
pushl $.LC0
call printf
addl $16, %esp
leal -4(%ebp), %eax
incl (%eax)
jmp .L2
.L3:
movl $0, %eax
leave
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)" 3、彙編階段 GCC把編譯產生的.s檔案轉換為目標檔案.o。這時使用-c選項就可以看到彙編代碼已經轉換成.o的目標代碼了。[armlinux@lqm program]$ gcc -c hello.s -o hello.o 4、連結階段 成功編譯之後,就進入了連結階段。這裡首先要明白“庫”的概念。這個程式中沒有“printf”的函數實現,且在預先處理階段包含進來的“stdio.h”中只有該函數的聲明,而沒有定義函數的實現。如何?“printf”?答案是:系統把這些函數的實現都做到名位libc.so.6的庫檔案裡了,沒有特別指定時,GCC會到預設的搜尋路徑“/usr/lib”下進行尋找。也就是連結到libc.so.6庫函數中去來實現函數“printf”,這就是連結的作用。 在RedHat 9下的函數庫如下: /lib:系統必備共用庫 /usr/lib:標準共用庫和靜態庫 /usr/X11R6/lib:X11R6的函數庫 /usr/local/lib:本地函數庫 標頭檔: /usr/include:系統標頭檔 /usr/local/include:本地標頭檔 在/etc/ld.so.conf中包含著共用庫的搜尋位置。我的libc.so.6在/lib下面,它是一個link,指向libc-2.3.2.so。這是因為我用的C庫--glibc版本是2.3.2。 函數庫一般可分為靜態庫和動態庫兩種。靜態庫是指連結時把庫檔案的代碼全部加到可執行檔中,因此產生的檔案比較大,但是在運行時就不再需要庫檔案了。其尾碼名一般為“.a”。動態庫在連結時並沒有把庫檔案的代碼加入到可執行檔中,而是在程式執行時由運行時連結檔案載入庫,這樣可以節省系統的開銷。動態庫一般尾碼名為“.so”。GCC在編譯時間預設使用動態庫。[armlinux@lqm program]$ gcc hello.o -o hello1
[armlinux@lqm program]$ ls -l hello1
-rwxrwxr-x 1 armlinux armlinux 11582 8月 28 17:42 hello1
[armlinux@lqm program]$ file hello1
hello1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not sd
[armlinux@lqm program]$ gcc -static hello.o -o hello2
[armlinux@lqm program]$ ls -l hello2
-rwxrwxr-x 1 armlinux armlinux 423442 8月 28 17:43 hello2
[armlinux@lqm program]$ file hello2
hello2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, statically linked, not stripped[armlinux@lqm program]$ ./hello1
Hello World 1 times!
Hello World 2 times!
Hello World 3 times!
Hello World 4 times!
Hello World 5 times!
Hello World 6 times!
Hello World 7 times!
Hello World 8 times!
[armlinux@lqm program]$ ./hello2
Hello World 1 times!
Hello World 2 times!
Hello World 3 times!
Hello World 4 times!
Hello World 5 times!
Hello World 6 times!
Hello World 7 times!
Hello World 8 times! hello2是靜態編譯,大小423442,是動態編譯的36.56倍。利用file可以看出此檔案statically linked,還是dynamically linked (uses shared libs)。 至此,GCC的整個編譯連結過程就完成了。
轉自http://hi.baidu.com/caibaihui/blog/item/01abdc8b7af0a317c9fc7ac4.html