最近看《UNIX系統編程》,感覺能把C語言用到這個水平,才能算是登堂入室。
一般來說,我們會把指針跟數組聯系起來理解,比如*p就是一個一維數組,**p是兩維數組等,而一般而言,見到兩維的指針也算是難得了,更高維的只怕看一會就會暈掉。《UNIX系統編程》中有個關於參數列表的例子,感覺對指針運用的已經到出神入化的境地,所以貼出來大家參考一下。
指向指針的多級指針
在C語言的入口main函數中,有一個**argv參數,指明命令行參數,一般寫法是這樣:
C代碼
- int main(int argc, char **argv){
- /*
- * code here.
- */
- }
- int main(int argc, char **argv){
- /*
- * code here.
- */
- }
這個**argv,是一個指向指針的指針,用來將命令行參數保存下來,比如,輸入一條命令:
prog -c -v 200
**argv中的內容即為 prog, -c, -v, 200. 因為prog, -c等的長度不等,就需要一個指針來引用他們,而prog後邊接幾個參數也是不定的,所以有需要有一個指針來引用,所以就是這裡的二維指針了。畫一個table可能看起來比較清晰一些:
prog -c -v 200
再考慮這樣一種情況,shell程序,對於你會輸多少行命令也是不知道的,那它就需要再多一個指針來引用你會有多少個命令輸入。這就是我們今天要看的(***ptr)了。
指向"指針的指針"的多級指針
書中的例子是這樣,先看下函數的原型:
C代碼
- int makeargv(const char *s, const char *delimiters, char ***argvp);
- int makeargv(const char *s, const char *delimiters, char ***argvp);
函數接受三個參數,第一個是要分析的串,第二個是界定符序列,第三個是生成的"指針的指針"(即二維數組)的指針。實現比較簡單,主要是看其中關於指針的用法:
C代碼
- /*
- * author : juntao.qiu
- */
- int makeargv(const char *s, const char *delimiters, char ***argvp){
- int error;
- int i;
- int numtokens;
- const char *snew;
- char *t;
- if((s == NULL) || (delimiters == NULL) || (argvp == NULL)){
- error = EINVAL;
- return -1;
- }
- *argvp = NULL;
- ssnew = s + strspn(s, delimiters);
- if((t = malloc(strlen(snew)+1)) == NULL)
- return -1;
- strcpy(t, snew);
- numtokens = 0;
- if(strtok(t, delimiters) != NULL)
- for(numtokens = 1; strtok(NULL, delimiters)!= NULL; numtokens++);
- if((*argvp = malloc((numtokens+1)*sizeof(char *))) == NULL){
- error = errno;
- free(t);
- errno = error;
- return -1;
- }
- if(numtokens == 0){
- free(t);
- }else{
- strcpy(t, snew);
- **argvp = strtok(t, delimiters);//注意此處的指針操作
- for(i = 1;i < numtokens;i++)
- *((*argvp)+i) = strtok(NULL, delimiters);//注意此處的指針操作
- }
- *((*argvp)+numtokens) = NULL;
- return numtokens;
- }
- /*
- * author : juntao.qiu
- */
- int makeargv(const char *s, const char *delimiters, char ***argvp){
- int error;
- int i;
- int numtokens;
- const char *snew;
- char *t;
- if((s == NULL) || (delimiters == NULL) || (argvp == NULL)){
- error = EINVAL;
- return -1;
- }
- *argvp = NULL;
- ssnew = s + strspn(s, delimiters);
- if((t = malloc(strlen(snew)+1)) == NULL)
- return -1;
- strcpy(t, snew);
- numtokens = 0;
- if(strtok(t, delimiters) != NULL)
- for(numtokens = 1; strtok(NULL, delimiters)!= NULL; numtokens++);
- if((*argvp = malloc((numtokens+1)*sizeof(char *))) == NULL){
- error = errno;
- free(t);
- errno = error;
- return -1;
- }
- if(numtokens == 0){
- free(t);
- }else{
- strcpy(t, snew);
- **argvp = strtok(t, delimiters);//注意此處的指針操作
- for(i = 1;i < numtokens;i++)
- *((*argvp)+i) = strtok(NULL, delimiters);//注意此處的指針操作
- }
- *((*argvp)+numtokens) = NULL;
- return numtokens;
- }
程序的主體比較簡單,就是按照傳入的s,按照界定符delimiters對其進行分割,分割完成後將其放在一個二維數組中,第一維表示最後數組,第二維表示第一個數組中每一個元素的值。
測試
好了,我們測試一下其運行情況:
C代碼
- int main(int argc, char **argv){
- char delim[] = " \t";
- int i;
- char **argvp;
- int numtokens;
- char *test = "mine -c 10 2.0";
- if((numtokens = makeargv(test, delim, &argvp)) == -1){
- fprintf(stderr, "failed to parse the string you given:%s\n", test);
- return 1;
- }
- printf("argument contains :\n");
- for(i = 0;i < numtokens;i++)
- printf("%d:%s\n", i, argvp[i]);
- return 0;
- }
- int main(int argc, char **argv){
- char delim[] = " \t";
- int i;
- char **argvp;
- int numtokens;
- char *test = "mine -c 10 2.0";
- if((numtokens = makeargv(test, delim, &argvp)) == -1){
- fprintf(stderr, "failed to parse the string you given:%s\n", test);
- return 1;
- }
- printf("argument contains :\n");
- for(i = 0;i < numtokens;i++)
- printf("%d:%s\n", i, argvp[i]);
- return 0;
- }
運行結果如下:
- C:\development\cpl\usp>ls
- Makefile a.exe makeargv.c nbproject
- C:\development\cpl\usp>a
- argument contains :
- 0:mine
- 1:-c
- 2:10
- 3:2.0
個人感覺,能把指多級指針用到這種熟練程度,才算是對C掌握了。《UNIX系統編程》中的代碼非常優雅,從大二一直讀到畢業,畢業後得空還在讀。我會盡量陸續把體會貼出來,以供參考。