用過gcc的都知道gcc有許多參數。例如要將hello.c譯成hello.exe並加上調試資訊,用gcc hello.c -g -o hello.exe即可。現在分析gcc的參數。對於這個例子,參數可分成三個部分:輸入檔案(hello.c)、是否包含調試資訊(-g)、輸出檔案 (-o hello.exe)。這三個部分的次序是無關緊要的,例如可以確保,-o後面跟的一定表示輸出檔案。
getopt是C語言標準庫中用來處理命令列參數的一個函數,其原型聲明在unistd.h檔案中:
int getopt( int argc, char *const argv[], const char *optstring );
這裡最關鍵的參數是optstring這個字串。假如我要做一個編譯器,僅僅支援-g -o參數,我需要把optstring設定為"go:",也就是把短線後的字母放在字串裡。但別忘了o後面的冒號":",冒號指出在這個參數的後面還有一個相關參數。從目的上看-o後面要跟一個輸出檔案名,否則是沒有意義的。optstring中的字母順序是任意的,本例中也可以寫成"o:g"。
getopt函數一個一個地處理命令列參數,當getopt返回-1時,表示所有的以短線標識的參數都已經處理完畢。因此通常使用一個迴圈來反覆調用getopt,並在迴圈裡面用一個switch語句來處理不同的參數。先看一下完整的程式:
#include <stdio.h>
#include <unistd.h>
struct globalOpt_t...{
int debug;
char *output;
char *input;
};
struct globalOpt_t globalOpt=...{0,NULL,NULL};
char *opt_string="go:";
int main(int argc, char **argv)...{
int opt;
while((opt=getopt(argc,argv,opt_string))!=-1)...{
switch (opt)...{
case 'g':
globalOpt.debug=1;
break;
case 'o':
globalOpt.output=optarg;
break;
}
}
globalOpt.input=argv[optind];
printf("Debug: %d/n",globalOpt.debug);
printf("Input: %s/n",globalOpt.input);
printf("Output: %s/n",globalOpt.output);
return 0;
}
注意到當處理-o參數時,用了globalOpt.output=optarg。optarg是getopt引入的一個全域變數,如果一個選項後面跟一個參數的話,optarg就是這個參數的值。對於本例來說optarg就是-o後面的輸出檔案名。迴圈的外面還有一句globalOpt.input=argv[optind],這是用來擷取輸入檔案名稱的。因為在這個例子中,前面不加選項參數的被認為是輸入檔案(gcc也是這樣設計的),無法用getopt來解析。這裡用到了optind這個全域變數,它表示再次調用getopt()時的下一個argv指標的索引。本例中,它將指向輸入檔案名稱參數的位置。
將以上程式編譯成mygcc.exe,在命令列下運行:mygcc -o hello.exe hello.c -g,將顯示:
Debug: 1
Input: hello.c
Output: hello.exe