在UNIX系統中,實現C源程序到可執行文件的這一轉換過程的工具是cc。在大多數系統中cc實際上是一個shell命令文件。有些系統中的C編譯程序可能並不叫cc而是其它的一個什麼名稱,如Sun工作站上常用的gcc等等。但這些都無關緊要。大多數系統中C編譯命令的用法基本上都是類似的。我們這裏介紹的將以SVR4上的C編譯系統爲基礎。
cc基本用法
一般我們只需要將C源程序的名字寫在CC命令行中,cc即可對這些源文件(.c文件)進行編譯。如果這些源文件中都沒有main()函數的定義,那麼cc將只能生成與各源文件相對應的目標文件(.o文件)。如果某個源文件中有main()函數的定義,則將把所有目標文件鏈接起來生成相應的可執行文件。缺省的情況下這個可執行文件的名字將是a.out。
例如,假定myprog.c是一個包含有main()函數定義的C語言程序文件,其中代碼如下:
/*********************************************
* An example source code with errors *
* Name:myprog.c *
*********************************************/
#include <stdio.h>;
#include <ctype.h>;
# define TESTOK 1
int TestInput(char* ValuInput)
{
while (*ValueInput)
if (!isdigit(*ValueInput))
return (! TESTOK);
else
*ValueInput++;
return ((100/atoi(ValueInput))? TESTOK:! TESTOK);
}
void main(int argc,char * argv[])
{
int i;
for (i=1; i<argc; i++)
if(TestInput (argv) == TESTOK)
printf("The %dth value '%s' /tis OK! /n",i,argv);
else
printf("The %dth value '%s' /tis BAD! /n" ,i,argv);
}
對於此程序中的錯誤(設計錯誤)我們暫不理會。下一章我們介紹程序調試時再回過頭來看看如何排除這個錯誤。
我們看到。在這個源程序文件中,定義了兩個函數:TestInput()和main(),定義了一個宏TESTOK,同時包含了兩個標準的頭文件。爲了把這個C程序轉換成可執行文件,在shell提示符下輸入:
$cc myprog.c
在程序中沒有任何語法錯誤的情況下,cc將在當前目錄下生成一個名爲a.out的可執行文件,如:
$cc myproc.c
$ ls -l
-rwx------ 1 yxz users 5812 Aug 31 15:32 a.out
-rw------- 1 yxz users 716 Aug 31 15:27 myproc.c
$
還可以看到這裏a.out是一個可執行文件。當然這個程序由於在設計上有些失誤,我們現在還不能馬上就帶參數運行。但不帶參數運行還是可以的。只不過此時該程序什麼都沒有幹,如:
$ a.out
$
在程序中我們通過main函數的兩個參數argc和argv而使程序能夠引用shell命令行參數;這是UNIX環境下一種常用的編程技術。
在生成了a.out文件之後,我們自然可用mv命令將其修改爲某個合適的名稱。但更簡單的方法是在cc命令行中加上-o選項,使cc直接將可執行文件寫入到指定的文件中而不生成a.out文件,如:
$ cc -o myprog myprog.c
$ ls -l myprog
total 14
-rwx------ 1 yxz users 5812 Aug 31 15:34 myprog
-rw------- 1 yxz users 716 Aug 31 15:27 myprog.c
$
我們看到,myprog這個文件除了文件名及修改時間同a.out不一樣外,其他屬性同a.out都是一摸一樣的。這也說明了兩者的等價性。
在某個程序的源代碼被存放到多個不同文件中的情況下,我們只需要在命令行中一一指定這多個C文件即可。例如,我們可以將上述myprog.c拆分爲兩個C文件和一個頭文件(.h)如下:
myprog.h
# include <stdio.h>;
# inclued <ctype.h>;
# define TESTOK 1
myprog.c
#include "myprog.h"
void main(int argc,char * argv[])
{
int i;
for (i=1;i<argc;i + +)
if (TestInput(argv)= = TESTOL)
printf("The %dth value '%s' /tis ok! /n",argv[1]);
else
printf("The %dth value ' %s' /tis BAD! /n",iargv);
}
myfunc.c
#include "myprog.h"
int TestInput(char * ValueInput)
{
while (* ValueInput)
if (!isdigit(*ValueInput)
return (! TESTOK);
else
ValueInput++;
return ((100/atoi(ValueInput))? TESTOK:! TESTOK);
}
這時要再編譯此程序時可輸入如下命令:
$ cc -o myprog myprog.c myfunc.c
在這個命令行中如果不指定myfunc.c,此時由於在myprog.c中所調用的TestInput()這個函數不是任何標準的庫函數,在鏈接時鏈接程序將找不到此符號的定義,故鏈接過程將以失敗而告終,此時cc將給出如下的錯誤信息:
Undefine first referenced
symbol in file
TestInput myprog.o
id: myprog:fatal error: Symbol referencing errors.No output written to myprog
$
而可執行文件myprog也無法生成。但編譯卻會生成myprog.c的目標代碼(在某個文件固有語法錯誤而無法正確被編譯的情況下(此時爲編譯過程出錯),cc將生成其他無語法錯誤的源文件的目標文件,但不進行鏈接)。如下:
$ ls -l
total 8
rw-r--r-- 1 yxz user 454 Sep 1 09:27 myfunc.c
rw-r--r-- 1 yxz user 479 Sep 1 09:28 myprog.c
rw-r--r-- 1 yxz user 298 Sep 1 09:27 myprog.h
rw-r--r-- 1 yxz user 924 Sep 1 09:28 myfunc.o
此時我們可以使用如下命令行得到可執行文件:
$ cc -o myprog myprog.o myfunc.c
這裏我們看到,cc命令行中的文件參數可以不全是.c文件,目標文件(.o)文件以後編譯過程中所得到的其他文件,如預編譯後文件(.i文件),編譯後的彙編程序(.s文件)等都可作爲文件參數。在瞭解了UNIX C編譯系統的工作過程之後,理解這一點是不困難的。因爲編譯系統只需要對各種不同類型的文件進行有關的處理就可以了。
關於cc命令最基本的用法我們就介紹這麼多,其它更高級的用法可參考下面幾節的討論。
常用選項
cc命令還提供了其他許多有用的命令行選項。藉助於這些選項我們可以對編譯過程進行進一步的控制,如使cc只完成某些階段的編譯工作,指定對頭文件的搜索目錄,指定對代碼進行優化,指定在代碼中加入一些供調試程序所用的信息,等等。下面我們分別討論這些問題。
1.僅進行編譯預處理
在命令行中加上-P選項可以使cc僅完成對.c文件的預處理工作,而後面的編譯,彙編,優化,鏈接則都不作,例如:
$ cc -P myprog.c
此時編譯系統將在當前目錄下生成一個名爲myprog.i的文件。這個文件中包含有對myprog.c中的僞指令進行處理後的代碼及myprog.c中原有的代碼。
在某些情況下,.i文件可能對於程序排錯有一定的用處。對於下面的代碼段,編譯程序可能會報告j無定義的錯誤:
for (i=0;i<10;i++)
{ /*declare avariable j:
int j;/*This is a temporary variable */
j=i*i;
.
.
.
}
預編譯處理後,缺省情況下,預處理程序將把源程序中的註釋刪除,這樣此段代碼將變成:
for (i=0;i<10;i++)
{
j=i*i;
.
.
.
}
這樣一來我們將能夠比較快地發現程序中的錯誤。利用.i文件,我們還能夠對條件編譯和宏擴展後的結果進行檢查。
在cc命令行中加上-C選項可以在預編譯後的文件中保留源文件中的註釋。
2.僅生成彙編語言代碼
在cc命令行中加上-S 選項,可以使cc只調用預處理程序和編譯程序以生成與源程序相應的彙編代碼。與每一個C源文件相應的彙編程序被放到相應的.s文件中。例如:
$ cc -S myprog.c myfunc.c
$ ls *.s
myfunc.s myprog.s
這種彙編語言代碼是同機器具體相關的。有些情況下我們可能需要用匯編語言進行編程,這時可以先用C語言編寫此程序,再編譯得到彙編程序,然後手工對此彙編程序修修改改,估計基本上就能滿足要求。由於用匯編語言進行編程是一件效率比較低的工作,用此種方法可以預期將獲得比較高的效率。
3.僅生成目標文件
如果只想生成源文件的目標代碼而不對這些代碼進行鏈接,可以在cc命令行中加上-C選項。此時編譯系將只生成與各源文件相對應的.o文件(目標文件)。如:
$ cc -c myprog.c myfunc.s
將生成同myprog.c和myfunc.s相對應的目標文件myprog.o和myfunc.o。
4.頭文件搜索路徑
當用戶在C源程序中用#include指令包含了某個頭文件時,根據文件名指定方法的不同,C編譯系統將在不同的目錄下去尋找指定的頭文件:
在用尖括號(<>;)指定頭文件名時,預處理程序將在系統中存放頭文件的標準位置(通常是/usr/include目錄)尋找指定的頭文件。
在用雙引號(“”)指定投文件名時,預處理程序將先在包含此頭文件的C源程序所在的目錄中(一般爲當前目錄),去查找該頭文件。找不到時再到標準目錄下去查找。
在對於那些頭文件既不在標準位置,又不在與C源程序同一目錄時的情況怎麼辦呢?爲此,CC命令提供了-I(Include)選項,以供用戶自己指定頭文件所在地目錄。例如,對於myfunc.c和myprog.c中所包含的頭文件myprog.h,我們假定其後來被放在目錄$HOME/include目錄下,而這兩個C文件則被放在$HOME/cfile目錄下。此時在$HOME/cfile目錄下對這兩個C文件進行編譯時,可使用如下命令:
$ cc -I #HOME/include myprog.c myfunc.c
此時對於這兩個C文件中的#include "myprog.h",預處理程序將先在$HOME/cfile目錄下,然後在$HOME/include目錄下,最後在系統標準位置查找myprog.h。-I選項也能改變那些用尖括號(<>指定的頭文件的搜索順序,此時預編譯程序將首先在-I指定的目錄下,然後纔在標準位置搜索。
-I選項可以多次重複使用。這樣我們將能夠指定多個非標準的頭文件目錄。
5.在目標文件中加入調試用的信息
除非是那種特別簡單的程序,一般大多數程序都會有這樣或那樣的問題。爲了能夠使用UNIX的符號調試程序(sdb,下一章會具體介紹)對程序進行調試,必須在目標代碼中加入一些有關的程序變量和語句信息,以便sdb能夠跟蹤函數調用、顯示變量的值以及設置斷點,等等。
在cc命令行中加入-g選項將能夠實現上述要求,如:
$ cc -g -o myprog myprog.c myfunc.c
這樣生成的myprog就可以用sdb進行調試了。
6.優化處理
優化的含義前面我們已經講過,這裏不想再重複。我們要說明的是在程序的調試過程中用不着進行優化處理。優化只應對最終提交的可執行程序進行。
在CC命令行中加上-O選項可以使編譯系統對代碼進行優化:
$ cc -O -o myprog myprog.c myfunc.c
優化對於不同的程序效果可能是不同的。有些程序優化不優化都不會有什麼區別。在有些系統上(如Sun OS),對程序的優化可以分成不同的級別(一般是1至4級)。第一級優化是僅在彙編級上優化,這是大多數系統都會做得。第二級優化是全局優化,如循環優化、公共子表達式的消除、複寫傳播及自動寄存器的分配。第三級上的優化再加上對外部變量的用法和定義的優化。第四級優化則在第三級基礎上對指針賦值的效果進行跟蹤。程序員可在-O後面加上一個數字(1,2,3,4)來表示所希望的優化級別。
在cc命令行中還可以使用其他的許多選項,下一節我們將介紹同鏈接有關的一些選項,其他選項的使用請參閱聯機幫助。或者使用手冊。
cc的基本用法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.