Posted on 2012-11-28 23:36
鑫龍 閱讀(199)
評論(0) 編輯 收藏 引用 所屬分類:
linux編程
對于很多初學《UNIX環境高級編程》(AdvancedProgramming in the UNIX Environment,簡稱APUE,以下使用簡稱)的朋友,第一個遇到的問題可能就是該書中的源代碼編譯的問題。此書中差不多每個例程中,都會有這樣一行源碼: #include "ourhdr.h"
在第二版中改為: #include "apue.h"
這個頭文件是作者把把每個例程中常用的標準頭文件,一些常用的出錯處理函數(err_**()之類的函數)和一些常用的宏定義給整理在一個頭文件中。這個可以省去在每個例程中錄入較多的重復代碼,這樣可以減少每個例程的長度。但是,這樣就給讀者帶來了不少麻煩。因為我們還要去搞明白如和把這個頭文件編譯,然后做成庫文件,添加到我們的系統中。特別讀于初學者,本來滿懷信心的,結果在編譯第一個程序的時候就出現了問題。我也沒有搞明白如何把"ourhdr.h"靜態的編譯到系統中。
不過,不明白如何使用"ourhdr.h"這個頭文件,并不會影響我們學習APUE,也不會影響我們編譯和運行每一個例程。其實,簡單的想一下,如果一個C程序要能順利的編譯和運行,除了我們要語法正確等方面外,最根本的是要保證我們程序中所調用的函數以及宏等等都要有完整的來源,也就是必須包含所有調用函數和宏所在的頭文件。對于一個具體的源程序,如果我們正確的包含了頭文件,那么剩下的就是程序本生語法方面應該注意的事項。
如何確定系統調用函數包含在那個頭文件中呢?這在Unix/Linux系統下并非一件難事。Unix/Linux下命令man可以幫助我們找到。man命令不僅可以幫助我們查找一般命令的用法,同時提供不同層次的幫助諸如系統調用或者管理員級別的命令等等(譬如FreeBSD6.1中,man1是用戶專用手冊,man 2是系統調用,man 3是庫函數查詢等等)。
下面我們就以APUE書中程序1-1(實現ls命令部分功能)為例,來說明如何將書中的程序改編成全部使用標準頭文件的程序。其中,操作系統用的是FreeBSD6.1,經過相應的修改可以在書中所說的幾個Unix系統及Linux系統中運行,我也曾在Debian Linux下成功編譯和運行該程序。書中1-1.c的原始代碼如下:
#include <sys/types.h> #include <dirent.h> #include "ourhdr.h"
int main(int argc, char *argv[]) { DIR *dp; struct dirent *dirp;
if (argc != 2) err_quit("usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL) err_sys("can't open %s", argv[1]); while ((dirp = readdir(dp)) != NULL) printf("%s\n", dirp->d_name);
closedir(dp); exit(0); } |
從書后面的附錄中可以看到"ourhdr.h"的內容比較多,包含了比較多的常用頭文件,一些宏定義和一些常用函數和出錯函數的定義。其實,對于每一個具體的程序,我們只需要找到該程序中用到的頭文件即可。
該1-1.c中所用到的系統函數調用有:opnedir(),readdir(),printf(),closedir()和exit()。 其中,對于常用的函數prinft()和exit(),它們所在的頭文件一般都知道,分別是<stdio.h>和<stdlib.h>。而對于opnedir(),readdir()和closedir(),我們可以通過man opendir,man readdir,manclosedir得到這三個關于目錄操作的函數所在的頭文件都是:<sys/types.h>和<dirent.h>。這兩個頭文件在源程序中也已經列出。
其次,1-1.c中還用到了作者自定義的兩個函數:err_quit()和err_sys()。這兩個函數主要使用來進行出錯處理的。當然,使用這兩個函數對錯誤信息的處理是比較完善的。但是,作為我們學習來講,了解程序的核心功能是首要的,我們可以將出錯處理簡化一點,即當遇到錯誤的時候,我們只簡單的使用printf()函數來提示一下有錯誤發生。當然,用printf()來進行出錯處理并不是一種很合理的方法,而且往往我們看不到更關鍵的錯誤信息,但對于我們僅僅作為學習來用還是可以接受的。畢竟我們要理解的核心部分是程序的功能實現,出錯處理在于其次。
通過以上的說明,我們可以將1-1.c修改為如下內容:
#include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { DIR *dp; struct dirent *dirp; if(argc != 2) { printf("You need input the directory name.\n"); exit(1); } if((dp = opendir(argv[1])) == NULL) { printf("cannot open %s\n", argv[1]); exit(1); }
while ((dirp = readdir(dp)) != NULL) printf("%s\n", dirp->d_name);
closedir(dp);
exit(0); }
|
這樣修改后的程序已經與作者的頭文件"ourhdr.h"沒有關系,可以單獨的進行編譯。我使用的是root用戶,執行命令:
# gcc 1-1.c //生成目標文件a.out 或者 # gcc -o 1-1 1-1.c //生成目標文件1-1
沒有任何錯誤和警告,說明編譯成功。這時我們執行生成的目標文件:
# ./a.out /home 或者 # ./1-1 /home
則會列出/home路徑下的所有文件,包括目錄(.)和(..)。
通過這樣的方法,基本上我們可以將該書中所有的例程修改成不包含"ourhdr.h"的程序。這樣,我們就可以單獨的編譯每一個例程,而不用顧及作者所給的雜湊的頭文件。同時這種比較笨的方法,反而有利于幫助我們了解不同系統調用所對應的頭文件,對于學習來說,這應該是一件好事。
歡迎交流,歡迎自由轉載,但請注明CU鏈接或本人CU Blog鏈接: http://blog.chinaunix.net/u/33048/showart_343553.html |