前言
C语言中指针,可以算是最灵活,最强大的地方,同时也是最艰深的地方。用不好的话,什么段错误,内存违例等以前没见过的东西都会跳出来。最近看《UNIX系统编程》 ,感觉能把C语言用到这个水平,才能算是登堂入室。
一般来说,我们会把指针跟数组联系起来理解,比如*p就是一个一维数组,**p是两维数组等,而一般而言,见到两维的指针也算是难得了,更高维的只怕看一会就会晕掉。《UNIX系统编程》 中有个关于参数列表的例子,感觉对指针运用的已经到出神入化的境地,所以贴出来大家参考一下。
指向指针的指针
在C语言的入口main函数中,有一个**argv参数,指明命令行参数,一般写法是这样:
- int main( int argc, char **argv){
-
-
-
- }
int main(int argc, char **argv){
/*
* code here.
*/
}
这个**argv,是一个指向指针的指针,用来将命令行参数保存下来,比如,输入一条命令:
prog -c -v 200
**argv中的内容即为 prog, -c, -v, 200. 因为prog, -c等的长度不等,就需要一个指针来引用他们,而prog后边接几个参数也是不定的,所以有需要有一个指针来引用,所以就是这里的二维指针了。画一个table可能看起来比较清晰一些:
再考虑这样一种情况,shell程序,对于你会输多少行命令也是不知道的,那它就需要再多一个指针来引用你会有多少个命令输入。这就是我们今天要看的(***ptr)了。
指向"指针的指针"的指针
书中的例子是这样,先看下函数的原型:
- int makeargv( const char *s, const char *delimiters, char ***argvp);
int makeargv(const char *s, const char *delimiters, char ***argvp);
函数接受三个参数,第一个是要分析的串,第二个是界定符序列,第三个是生成的"指针的指针"(即二维数组)的指针。实现比较简单,主要是看其中关于指针的用法:
-
-
-
- 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;
}
程序的主体比较简单,就是按照传入的s,按照界定符delimiters对其进行分割,分割完成后将其放在一个二维数组中,第一维表示最后数组,第二维表示第一个数组中每一个元素的值。
测试
好了,我们测试一下其运行情况:
- 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系统编程》中的代码非常优雅,从大二一直读到毕业,毕业后得空还在读。我会尽量陆续把体会贴出来,以供参考。
【作者】JavaEye网站的abruzzi