`
abruzzi
  • 浏览: 452343 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

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

分享到:
评论
35 楼 mikeandmore 2009-06-29  
night_stalker 写道
星号经常会导致一些 syntax error,得弄很多 typedef 才行 ……
LPCSTR argv[] ……

其实 char[][] 是可以的 …… 至少 gcc 可以
#include <stdio.h>
int main(int argc, char argv[][]) {
	printf("%s\n", ((char**)argv)[0]);
	return 0;
}


mikeandmore% gcc test.c
test.c:2: error: array type has incomplete element type

mikeandmore% gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.3-13' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.3 (Debian 4.3.3-13)
34 楼 rain2005 2009-06-29  
abruzzi 写道
rain2005 写道
最近linux内核代码看的比较多,看了大家好多的发言,感觉大家对指针的概念还比较模糊,其实要弄清楚指针怎么回事必须和汇编指令结合起来,比如汇编编译后的机器指令是没有指针的概念的,指针就是地址,不同类型的指针就是地址指向的内容或者说大小(字节)不同而已,多重指针也很好理解,就是指针变量指向的内容还是地址。说到指针有人就会拿来和数组比较,其实数组是C抽象的概念,到编译器那里全变成了指针。只不过是一段连续的存储单元。说了这么多可能大家都明白,推荐大家看下《深入理解计算机系统》第3章,看看C是编译成汇编是怎么回事对指针的理解会更加深刻。

不是每个人可以轻松的阅读Linux内核这样的大工程的,当然有能力读会对C理解的很深入,我的意思是最好通过一些小的例子来说明一些原理,这样对以后学习大型的项目有很大的帮助。
内核中的一个文件,对外部的依赖肯定是千丝万缕的,不容易上手,呵呵。

既然说到linux内核的阅读,其实现在的Linux内核确实复杂到了难以想象的地步,不过Linux 0.01却不到3万行代码,而且选对书也很重要了,我看的是《linux内核完全剖析》,用了2个半月查不多看完了,当然前期的知识准备过程还有2个月,其中就包括《深入理解计算机系统》这本书,其实如果是专业C开发人员的话理解内核对大型项目我感觉还是很重要的,更何况我一个java程序员学习了内核之后对线程,并发有了新的理解,当然这只是一小块收获。
33 楼 abruzzi 2009-06-29  
rain2005 写道
最近linux内核代码看的比较多,看了大家好多的发言,感觉大家对指针的概念还比较模糊,其实要弄清楚指针怎么回事必须和汇编指令结合起来,比如汇编编译后的机器指令是没有指针的概念的,指针就是地址,不同类型的指针就是地址指向的内容或者说大小(字节)不同而已,多重指针也很好理解,就是指针变量指向的内容还是地址。说到指针有人就会拿来和数组比较,其实数组是C抽象的概念,到编译器那里全变成了指针。只不过是一段连续的存储单元。说了这么多可能大家都明白,推荐大家看下《深入理解计算机系统》第3章,看看C是编译成汇编是怎么回事对指针的理解会更加深刻。

不是每个人可以轻松的阅读Linux内核这样的大工程的,当然有能力读会对C理解的很深入,我的意思是最好通过一些小的例子来说明一些原理,这样对以后学习大型的项目有很大的帮助。
内核中的一个文件,对外部的依赖肯定是千丝万缕的,不容易上手,呵呵。
32 楼 rain2005 2009-06-29  
最近linux内核代码看的比较多,看了大家好多的发言,感觉大家对指针的概念还比较模糊,其实要弄清楚指针怎么回事必须和汇编指令结合起来,比如汇编编译后的机器指令是没有指针的概念的,指针就是地址,不同类型的指针就是地址指向的内容或者说大小(字节)不同而已,多重指针也很好理解,就是指针变量指向的内容还是地址。说到指针有人就会拿来和数组比较,其实数组是C抽象的概念,到编译器那里全变成了指针。只不过是一段连续的存储单元。说了这么多可能大家都明白,推荐大家看下《深入理解计算机系统》第3章,看看C是编译成汇编是怎么回事对指针的理解会更加深刻。
31 楼 abruzzi 2009-06-29  
oxromantic 写道

向你这样半吊子而且狂妄的人不多了
复制一段代码:
void freemakeargv(char **argv) {
   if (argv == NULL)
      return;
   if (*argv != NULL)
      free(*argv);
   free(argv);
}
请问这个代码在你的测试里有涉及吗?
非得我挑明了说,真是笑话


笑话?不知道好笑在哪儿?好像加入free了就牛B了似的。主题是多级指针的操作,不是面向内存保护,当然,这段代码确实属于严格的写法,之所以没有写出来不是因为其不重要,而是在这个例子中不重要。

另:我的半吊子不半吊恐怕还轮不到你说,至于狂妄,在下还真是不敢,草莽之间,卧虎藏龙之辈正不知有多少,哈哈。
30 楼 oxromantic 2009-06-29  
abruzzi 写道
oxromantic 写道
没有用到&,指针基础才涉及一半,
而且不考虑初级还是高级,希望LZ过一年再来看自己的代码,能写出这种内存使用代码的人也能如此狂妄,怪不得开源社区一直高质量代码百分比不高,原来多亏LZ你啊


大侠,你看完帖子了没有就在这儿喊?如果是看完了,那只能说你不识字了,我早就说明那个是《UNIX系统编程》中的一个例子,写的很好,所以贴出来大家学习下。不是我写的,再说了,说这个代码写的烂的人只不过是在暴露自己不学无术的真面目而已。

已经说了,文章的题目起的有点大了,例子给的比较简单,不好意思。

向你这样半吊子而且狂妄的人不多了
复制一段代码:
void freemakeargv(char **argv) {
   if (argv == NULL)
      return;
   if (*argv != NULL)
      free(*argv);
   free(argv);
}
请问这个代码在你的测试里有涉及吗?
非得我挑明了说,真是笑话
29 楼 night_stalker 2009-06-29  
星号经常会导致一些 syntax error,得弄很多 typedef 才行 ……
LPCSTR argv[] ……

其实 char[][] 是可以的 …… 至少 gcc 可以
#include <stdio.h>
int main(int argc, char argv[][]) {
	printf("%s\n", ((char**)argv)[0]);
	return 0;
}
28 楼 abruzzi 2009-06-29  
ray_linn 写道
看到有人把char* argv[]写成char **argv,我就想拿本书往他头上劈头盖脑的打下去。

char* argv[]原来是多么好理解的事情,非得弄复杂了。。。


能看到的几乎所有的代码的main函数都是这样写的,你去盖他们脸吧,小心闪了腰!
27 楼 abruzzi 2009-06-29  
oxromantic 写道
没有用到&,指针基础才涉及一半,
而且不考虑初级还是高级,希望LZ过一年再来看自己的代码,能写出这种内存使用代码的人也能如此狂妄,怪不得开源社区一直高质量代码百分比不高,原来多亏LZ你啊


大侠,你看完帖子了没有就在这儿喊?如果是看完了,那只能说你不识字了,我早就说明那个是《UNIX系统编程》中的一个例子,写的很好,所以贴出来大家学习下。不是我写的,再说了,说这个代码写的烂的人只不过是在暴露自己不学无术的真面目而已。

已经说了,文章的题目起的有点大了,例子给的比较简单,不好意思。
26 楼 ray_linn 2009-06-29  
看到有人把char* argv[]写成char **argv,我就想拿本书往他头上劈头盖脑的打下去。

char* argv[]原来是多么好理解的事情,非得弄复杂了。。。
25 楼 oxromantic 2009-06-29  
没有用到&,指针基础才涉及一半,

abruzzi 写道
diogin 写道
abruzzi 写道
ray_linn 写道
这叫高级??????。。。。。。

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


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

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

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


你这例子就是入门级别的,不要自认为高级。
学无止境,多拿些开源代码看看,体会下什么样的用法叫做“高级”。


完全没有我的例子有多么“高级”的意思,我也没有说我的例子不是入门级的,本来就是嘛,如果你可以通过例子学习到多级指针的用法,那是最好的了,也正是在下的目的所在。C中的指针难以驾驭是肯定的,你可以自己写着试一下,可能给出的例子对于这个题目来说有点不准确。

至于后边的什么“学无止境,多拿些开源代码看看,体会下什么样的用法叫做“高级”。”之类,说句不好听的,我看开源代码,给开源社区贡献代码的时候,你还不知道在哪儿卖鱼蛋呢。

哈哈,玩笑,纯属玩笑。

而且不考虑初级还是高级,希望LZ过一年再来看自己的代码,能写出这种内存使用代码的人也能如此狂妄,怪不得开源社区一直高质量代码百分比不高,原来多亏LZ你啊
24 楼 justshare 2009-06-29  
<p>写得不错,再接再励,但是纠正一个错误,也可以说是拘泥于语法罢</p>
<div class="quote_div">
<p>函数接受三个参数,第一个是要分析的串,第二个是界定符序列,<strong>第三个是生成的"指针的指针"(即二维数组)的指针</strong>。实现比较简单,主要是看其中关于指针的用法:</p>
</div>
<p>粗体字这里,我觉得char **argv应该是一维数组,只不过它里面的每一个元素是一个字符串而已</p>
<p>char **argv &lt;=&gt; char *argv[] = {"aaaaa", "bbbbb", "cccccc"};</p>
<p> </p>
<p>二维数组:</p>
<p>char ***argv &lt;=&gt; char *[][3] = {</p>
<p>{"aaaa", "bbb", "cccc"},</p>
<p>{"1111", "2222"},</p>
<p>}</p>
<p> </p>
<p><strong>望指正</strong></p>
23 楼 abruzzi 2009-06-29  
好了好了,我现在郑重声明,本文严重的文不对题,不用讨论啦。哈哈,一不小心几乎成标题党了!感谢各位的留言。
另:如果能从中学到东西,那是最好,如果觉得太初级,请关闭本页,呵呵。
22 楼 abruzzi 2009-06-29  
wq163 写道
我看出来了,lz的意思是指针级数越高 表明用法就越高级了,哈哈

明摆的事儿么,一级的指针还有什么高级可言??对于多级指针的引用容易乱掉,所以拿个例子来看,有什么问题??
21 楼 wq163 2009-06-29  
我看出来了,lz的意思是指针级数越高 表明用法就越高级了,哈哈
20 楼 chirking 2009-06-29  
<div class="quote_title">troyconder 写道</div>
<div class="quote_div">指针是把双刃剑,弊大于利的那种瑕疵剑。</div>
<p> </p>
<p>童鞋,地球很危险,回火星去吧,或者JAVA星!</p>
19 楼 chirking 2009-06-29  
<div class="quote_title">bachmozart 写道</div>
<div class="quote_div">
<div class="quote_title">diogin 写道</div>
<div class="quote_div">
<div class="quote_title">abruzzi 写道</div>
<div class="quote_div">
<div class="quote_title">mikeandmore 写道</div>
<div class="quote_div">那个啥。。。原型难道不是<br>int main(int argc, char* argv[]);<br>???</div>
<br>都说了,可以把指针理解成数组(当然不是完全等价),所以 <br>char *argv[] == char **argv == char argv[][], 你可以分别试一下。</div>
<br><br>指针 != 数组。<br><br>找本《C专家编程》看看</div>
</div>
<p>lz就是想给新手们讲一下分享一下自己学习心得,你们这些高人都来凑什么热闹。。。</p>
<p>K&R里讲得很明白了,指针等价于数组,(翻页)当作为函数参数时。</p>
<p>那个万恶的翻页啊。。。</p>
<p>想到个笑话:投案自首是犯罪  (墙角转弯) 分子的唯一出路</p>
<p>囧。</p>
<p> </p>
18 楼 troyconder 2009-06-29  
指针是把双刃剑,弊大于利的那种瑕疵剑。
17 楼 mikeandmore 2009-06-29  
abruzzi 写道
mikeandmore 写道

不过xxx[][]这种写法无论如何都是没有的。。。

这个只不过是你没有见过而已,不是没有,你可以写个代码测一下。而且有很多例子中都有这种形式的。


....
这个明显是语法错误么。。。搞毛。。。
N维数组要指定N-1个最大维数。。。否则无法寻址。
16 楼 zhongshan99 2009-06-29  
文不对题,标题是“C语言中指针的高级用法”,内容却是最基础的应用。。。

相关推荐

    利用C语言的多级指针创建三维动态数组

    利用C语言的多级指针创建了三维动态数组,并操纵数组.最后释放三级指针.这个例子展示了C语言指针功能的强大、灵活与“危险”。

    C语言多级指针专项详解图示分析程序代码

    C语言多级指针专项详解图示分析程序代码 在本文中,我们将详细讲解C语言中的多级指针概念,并提供实践代码和图示分析来帮助读者更好地理解。 基础知识 在C语言中,指针其实就是地址,所谓的地址就是由操作系统...

    C语言中的指针学习 C语言中的指针学习

    对于更复杂的指针类型,如指向指针的指针,它们的应用可能包括多级指针结构的构建,以及在函数之间传递复杂数据结构的能力。例如,通过二级指针可以在函数间共享并修改指针的值,从而实现更高效的数据处理和内存管理...

    C语言中的指针

    C语言还支持多级指针,例如`int **ptr`是一个指向指针的指针。这在处理动态内存分配、链表和树等复杂数据结构时非常有用。 七、指针与内存管理 指针与`malloc()`和`free()`函数结合使用,可以实现动态内存分配和...

    C语言,多级菜单实现思路

    在C语言中,构建多级菜单系统通常涉及到数据结构的设计、用户输入的处理以及程序逻辑的组织等方面。 1. 多级菜单的实现方法:多级菜单在用户界面中用于展现层次化选项,用户可以通过选择菜单项进入下一级菜单,直至...

    C语言中的指针学习.rar

    - 通过多级指针,我们可以间接访问嵌套结构中的元素。 7. **动态内存分配与释放**: - 使用`malloc()`函数可以动态地在运行时分配内存,返回的指针指向分配的内存区域。 - 使用`free()`函数可以释放不再需要的...

    c语言中的指针

    多级指针是指指向指针的指针,例如`int **p`。这种类型的指针可以用来间接访问嵌套结构中的数据,或者在函数之间传递对指针的引用。 9. 指针和结构体: 指针也可以用于指向结构体,使得我们可以轻松地访问和修改...

    C语言-- 指针经典趣谈

    多级指针是指指向指针的指针,如int **pptr。这种类型的指针常用于处理二维数组或实现复杂的数据结构,如链表。例如,int arr[2][2]={{1,2},{3,4}}; int **pptr = arr; 这里,pptr指向arr的第一个元素,即一个int *...

    c语言指针,指针与变量,指针与指针

    最后,我们来谈谈指向指针的指针,也称为二级指针或多级指针。这是一种更高级的概念,用于管理指向其他指针的指针。例如,`int **pptr;`声明了一个指向`int *`类型的指针。这在处理动态内存分配、函数返回指针的指针...

    彻底搞定C语言中的指针问题

    总结来说,掌握C语言中的指针涉及理解指针的定义、如何通过指针传递函数参数以及如何使用多级指针。这需要通过实践和理解指针的底层工作原理来实现。通过学习和熟练运用这些概念,你将能够编写出更加高效和灵活的...

    C语言中指针基本概念及应用详解

    内容概要:本文详细讲解了C语言中的指针概念及其应用技巧,涵盖指针的基本概念、初始化与赋值、访问变量的值、与数组的关系、指针的运算以及多级指针等内容,并提供了多个实例帮助读者加深理解和掌握。 适用人群:...

    C语言指向指针的指针

    这种指针的指针通常被称为多级指针,其中二级指针是指向指针的指针,三级指针是指向二级指针的指针,以此类推。 首先,让我们理解一级指针。一级指针是一个普通指针,它存储了其他变量的地址。例如,假设有一个整型...

    c语言:我眼中的指针(教你彻底认识指针)

    多级指针是指针的指针,即指针本身也是一个变量的地址。 ##### 1. 双重指针 双重指针是指向指针的指针,定义格式为: ```c type **pointer_name; ``` 例如: - `int **ptr;` 定义了一个指向整型指针的指针 `ptr`...

    C语言经典指针与数组ppt

    - **多级指针**:指针可以指向指针,形成多级指针,如`int **pp`,这在处理复杂数据结构时非常有用。 3. **数组与指针的关系** - **数组名作为指针**:在很多情况下,数组名可以被视为指向数组首元素的指针。例如...

    C语言中的指针_JewinH的博客-CSDN博客_指针c语言.pdf

    指针还可以被声明为多级指针,例如指向指针的指针: ```c int **pp; // 声明一个二级指针,它指向一个指针,这个指针又指向一个整型变量 ``` 这样,我们可以通过`**`来访问最终的变量值。此外,指针可以参与算术...

    C语言中指针的灵活运用

    此外,多级指针允许我们指向指向其他指针的指针,这在实现递归数据结构或复杂的数据结构如链表时非常常见。 总之,C语言中的指针是编程中的强大工具,它们使得程序能够更高效地访问和操作内存,实现更灵活的数据...

    c语言程序设计指针参考

    C语言支持多级指针,这意味着指针可以指向其他指针。例如,`int **pptr`是一个指向`int *`类型指针的指针。这在处理复杂的数据结构如链表、树等时非常有用。 6. **函数参数中的指针** C语言中的函数可以通过指针...

    C语言中有关指针内容讲课思路的探讨.pdf

    在讲解内容的选择上,应当从基础概念出发,逐步引入复杂概念,如多级指针、指针数组和指针作为函数返回值。讲解方法可采用理论结合实践,让学生编写和调试包含指针的程序,以巩固理解和技能。 总结,讲解C语言中的...

    c语言指针的一些用法

    C语言支持多级指针,即指针指向另一个指针。这在传递函数参数、动态内存管理等场景中很常见: ```c int **pp; int *p = malloc(sizeof(int)); // 动态分配内存 *p = 10; pp = &p; // pp是一个指向指针的指针,...

Global site tag (gtag.js) - Google Analytics