前言
C語(yǔ)言中指針,可以算是最靈活,最強大的地方,同時(shí)也是最艱深的地方。用不好的話(huà),什么段錯誤,內存違例等以前沒(méi)見(jiàn)過(guò)的東西都會(huì )跳出來(lái)。最近看《UNIX系統編程》,感覺(jué)能把C語(yǔ)言用到這個(gè)水平,才能算是登堂入室。
一般來(lái)說(shuō),我們會(huì )把指針跟數組聯(lián)系起來(lái)理解,比如*p就是一個(gè)一維數組,**p是兩維數組等,而一般而言,見(jiàn)到兩維的指針也算是難得了,更高維的只怕看一會(huì )就會(huì )暈掉。《UNIX系統編程》中有個(gè)關(guān)于參數列表的例子,感覺(jué)對指針運用的已經(jīng)到出神入化的境地,所以貼出來(lái)大家參考一下。
指向指針的指針
在C語(yǔ)言的入口main函數中,有一個(gè)**argv參數,指明命令行參數,一般寫(xiě)法是這樣:
- int main(int argc, char **argv){
-
-
-
- }
int main(int argc, char **argv){/** code here.*/}
這個(gè)**argv,是一個(gè)指向指針的指針,用來(lái)將命令行參數保存下來(lái),比如,輸入一條命令:
prog -c -v 200
**argv中的內容即為 prog, -c, -v, 200. 因為prog, -c等的長(cháng)度不等,就需要一個(gè)指針來(lái)引用他們,而prog后邊接幾個(gè)參數也是不定的,所以有需要有一個(gè)指針來(lái)引用,所以就是這里的二維指針了。畫(huà)一個(gè)table可能看起來(lái)比較清晰一些:
再考慮這樣一種情況,shell程序,對于你會(huì )輸多少行命令也是不知道的,那它就需要再多一個(gè)指針來(lái)引用你會(huì )有多少個(gè)命令輸入。這就是我們今天要看的(***ptr)了。
指向"指針的指針"的指針
書(shū)中的例子是這樣,先看下函數的原型:
- int makeargv(const char *s, const char *delimiters, char ***argvp);
int makeargv(const char *s, const char *delimiters, char ***argvp);
函數接受三個(gè)參數,第一個(gè)是要分析的串,第二個(gè)是界定符序列,第三個(gè)是生成的"指針的指針"(即二維數組)的指針。實(shí)現比較簡(jiǎn)單,主要是看其中關(guān)于指針的用法:
-
-
-
- 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;
- snew = 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;snew = 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;}
程序的主體比較簡(jiǎn)單,就是按照傳入的s,按照界定符delimiters對其進(jìn)行分割,分割完成后將其放在一個(gè)二維數組中,第一維表示最后數組,第二維表示第一個(gè)數組中每一個(gè)元素的值。
測試
好了,我們測試一下其運行情況:
- 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
個(gè)人感覺(jué),能把指針用到這種熟練程度,才算是對C掌握了?!禪NIX系統編程》中的代碼非常優(yōu)雅,從大二一直讀到畢業(yè),畢業(yè)后得空還在讀。我會(huì )盡量陸續把體會(huì )貼出來(lái),以供參考。