论坛首页 编程语言技术论坛

C语言中的多级指针

浏览 27190 次
精华帖 (2) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-06-27   最后修改:2009-06-29
C

前言

C语言中指针,可以算是最灵活,最强大的地方,同时也是最艰深的地方。用不好的话,什么段错误,内存违例等以前没见过的东西都会跳出来。最近看《UNIX系统编程》,感觉能把C语言用到这个水平,才能算是登堂入室。

一般来说,我们会把指针跟数组联系起来理解,比如*p就是一个一维数组,**p是两维数组等,而一般而言,见到两维的指针也算是难得了,更高维的只怕看一会就会晕掉。《UNIX系统编程》中有个关于参数列表的例子,感觉对指针运用的已经到出神入化的境地,所以贴出来大家参考一下。

 

指向指针的指针

在C语言的入口main函数中,有一个**argv参数,指明命令行参数,一般写法是这样:

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)了。

 

指向"指针的指针"的指针

书中的例子是这样,先看下函数的原型: 

int makeargv(const char *s, const char *delimiters, char ***argvp);

 

函数接受三个参数,第一个是要分析的串,第二个是界定符序列,第三个是生成的"指针的指针"(即二维数组)的指针。实现比较简单,主要是看其中关于指针的用法:

/*
 * 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;
}

  

运行结果如下:

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系统编程》中的代码非常优雅,从大二一直读到毕业,毕业后得空还在读。我会尽量陆续把体会贴出来,以供参考。

   发表时间:2009-06-28  
这叫高级??????。。。。。。


我发现现在有越来越多人喜欢弄些*******,.......当成优雅的趋势。
0 请登录后投票
   发表时间:2009-06-28  
ray_linn 写道
这叫高级??????。。。。。。

我发现现在有越来越多人喜欢弄些*******,.......当成优雅的趋势。


一个盛满了水的杯子要再盛水,就要把原先的水先全部倒光。

文中的指针的用法,应该已经不算是入门级别的了。当然,例子可能有点不够复杂,没有函数指针,函数指针表(指向函数指针的指针),但是经验中,2层以上的指针就比较难控制了,而这个例子可以很好的看出对3层指针的控制过程,不知道这位大侠心目中的“高级”是什么样子的?

如有必要,大可以在后边略作叙述,指教一二,那是欢迎之至,但是这种冷嘲热讽,就难免不知其所云了。
0 请登录后投票
   发表时间:2009-06-28  
那个啥。。。原型难道不是
int main(int argc, char* argv[]);
???
0 请登录后投票
   发表时间:2009-06-28  
mikeandmore 写道
那个啥。。。原型难道不是
int main(int argc, char* argv[]);
???

都说了,可以把指针理解成数组(当然不是完全等价),所以
char *argv[] == char **argv == char argv[][], 你可以分别试一下。
0 请登录后投票
   发表时间:2009-06-28  
abruzzi 写道
ray_linn 写道
这叫高级??????。。。。。。

我发现现在有越来越多人喜欢弄些*******,.......当成优雅的趋势。


一个盛满了水的杯子要再盛水,就要把原先的水先全部倒光。

文中的指针的用法,应该已经不算是入门级别的了。当然,例子可能有点不够复杂,没有函数指针,函数指针表(指向函数指针的指针),但是经验中,2层以上的指针就比较难控制了,而这个例子可以很好的看出对3层指针的控制过程,不知道这位大侠心目中的“高级”是什么样子的?

如有必要,大可以在后边略作叙述,指教一二,那是欢迎之至,但是这种冷嘲热讽,就难免不知其所云了。


你这例子就是入门级别的,不要自认为高级。
学无止境,多拿些开源代码看看,体会下什么样的用法叫做“高级”。
0 请登录后投票
   发表时间:2009-06-28  
abruzzi 写道
mikeandmore 写道
那个啥。。。原型难道不是
int main(int argc, char* argv[]);
???

都说了,可以把指针理解成数组(当然不是完全等价),所以
char *argv[] == char **argv == char argv[][], 你可以分别试一下。


指针 != 数组。

找本《C专家编程》看看吧
0 请登录后投票
   发表时间:2009-06-28   最后修改:2009-06-28
diogin 写道
abruzzi 写道
mikeandmore 写道
那个啥。。。原型难道不是
int main(int argc, char* argv[]);
???

都说了,可以把指针理解成数组(当然不是完全等价),所以
char *argv[] == char **argv == char argv[][], 你可以分别试一下。


指针 != 数组。

找本《C专家编程》看看吧


人家已经说了 “不是完全等价”,还在这里纠缠,
C专家编程也就是本入门书,里面没一样东西真正讲的深入的

不过我确实觉得纠缠在语法上没啥必要,如何解决问题才是重要的,与其说理解C的指针不如说去理解内存模型等东西,研究研究内存管理还是挺有意思的,比如写个dlmalloc库什么的
0 请登录后投票
   发表时间:2009-06-28  
还行 都别说lz了 毕竟人家是来分享经验的
0 请登录后投票
   发表时间:2009-06-28   最后修改:2009-06-28
扯些函数指针的事情 ……

万能函数指针。例如:
int(...)

成员函数指针(MSVC 可以去掉 &):
&SomeClass::func

静态成员指针的用法和普通的函数指针一样。

但是非静态成员函数指针得用丑陋难用的 mem_fun_ref<.......... 转换 ……

从实例得到函数指针(->* 和 .*):
SomeClass* x = new SomeClass();
auto f = x->*func;
SomeClass y;
auto g = y.*func;

注:auto 关键字得在 GCC 4.4 或者 msvc 10 以后的版本用。

继承后的非静态成员函数指针很复杂,每个编译器处理方式不一样,最好别碰 ……
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics