C语言指针系列——并不可怕的声明
Posted TigerMee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言指针系列——并不可怕的声明相关的知识,希望对你有一定的参考价值。
指针就像双节棍,指针的指针就像三节棍,依此类对至n节棍。。。
指针的数组,呃,就像某种伤不起的棍。。。
C语言里面最灵活、有时候也是最让人莫名的就是指针了,以至于专门有一本书叫《C和指针》。尤其是指针的声明,指向整型的指针,指向数组的指针,指向函数的指针,再加上n多个括号的组合,有时候真让人摸不到头脑。其实,理解指针声明的关键首先在于记住*, [], ()操作符的优先级和结合性;然后由指针名称开始由内向外层层解剖,再复杂的声明也不难解释。本文使用一种自创的图示的方法,来清晰地展示指针声明的根本。
首先我们来看一下与指针声明相关的一些运算符的优先级:
(优先级高低由上至下,即上面的操作符的优先级高于下面的运算符的优先级。)
操作符 | 描述 | 结合性 |
---|---|---|
() | 函数调用 | L-R |
[] | 下标引用 | L-R |
* | 间接访问 | R-L |
请务必记住以上运算符的优先级,下面我们来看几个例子。为了能更形象地说明指针声明,我们使用图示法。
1. 基本数据类型的指针。
让我们从最简单的开始,相信大家都能说出这个声明中var的意思。
a) int *var;
显然,var是一个指向整型的指针,图示如下:
2. “数组的指针”和“指针的数组”
经过热身,来看一下数组的指针。
a) int (*var)[6];
根据“由内向外”的原则,我们层层分析。
第一层:(*var),表示对var进行解引用,那么var肯定是一个指针。那么它指向什么呢?接着看。(下面用xxx表示(*var))。
第二层:int xxx[6],Ok,看来xxx是一个数组,就是说对var进行解引用后得到一个数组,并且数组的元素是int。
结论:到此为止,这个表达式就解释完了。var是一个指向数组的指针,指向的数组有6个元素,并且数组元素的类型是int。
有了上面的基础,相信下面的声明对你也不难了:
b) int *(*var)[6];
第一层:与上面的示例一样,var是一个指针。
第二层:int *xxx[6],xxx是什么呢?又有*操作符,又有[]操作符,应该先解释那个呢?上文提到的操作符的优先级这里要发挥作用了。[]操作符的优先级高于*操作符,所以xxx先与[]结合,那么xxx是一个数组,数组的元素类型是int *,即指向整型的指针。
结论:var是一个指向数组的指针,指向的数组有6个元素,并且数组元素的类型是int *,即指向整型的指针。
如果你感觉对数组和指针足够清晰了,来看一下下面的声明:
c) int *var[6];
这个貌似比上面的简单多了,不过不要上当哦,var这里可不一定是指针。
第一层:*和[]同时出现,还是先看操作符的优先级,[]高于*,所以var先与[]结合,因此var是个数组。
第二层:对var进行下标引用后,得到数组的一个元素xxx。int *xxx,xxx是个指向整型的指针。原来数组的元素是int *,即指向整型的指针。
结论:var是一个数组,数组有6个元素,数组元素的类型是int *。
3. 函数的指针
从某个意义上说,函数指针是指针中的精髓,提供了极大的便利性,同时也引起了足够的混乱,函数的指针要是再和数组结合起来,可真是乱上加乱。不过没关系,只要我们牢记运算符的优先级,使用由内至外的方法,一样能够清晰地解释。话不多说,来看示例。
(为了简化声明,更突出声明中的指针,如果函数没有参数,则省略了函数的参数类型,即将func(void)记为func()。在实际编程中,强烈建议你使用函数参数void,明确标示该函数没有参数)
a) int (*var)();
第一层:(*var), var是一个指针,指向什么呢?接着看。
第二层:int xxx(),xxx是一个函数,所以var指向的是一个函数。
结论:var是一个函数指针,指向一个函数,并且,函数的参数类型是void,返回值类型是int。
有了上面的例子做参考,相信这个也不难。
b) int *(*var)();
第一层:(*var), var是一个指针。
第二层:int * xxx(),xxx是一个函数,所以var指向的是一个函数。
结论:var是一个函数指针,指向一个函数,并且,函数的参数类型是void,返回值类型是int *。
不要被迷惑哦,来看这个。
c) int *var();
注意()和*操作符的优先级,()操作符高于*,优先计算。
到这里相信大家都能理解了,var只是一个函数,不是指针。
结论:var是一个函数,函数的参数为void,返回值为int *。
如果你认为已经对函数的指针足够了解了,那么来试试下面这个。
d) int *(*var())();
还是第一次在一个表达式中出现两个()操作符,不怕,还是两大法宝:操作符优先级,由内向外。
第一层:(*var()),()操作符优先级高于*,所以先计算var(),再对结果与*操作符做运算。所以var是一个函数!对函数的返回值可以做解引用操作,所以函数的返回值是一个指针,指向什么呢?往下看。
第二层:int *xxx(),看看上面的示例c,不陌生吧,xxx就是一个返回值为int *的函数。
结论:var是一个函数,函数的参数为void,返回值为一个函数指针,指向的函数参数为void,返回值为int *。
细心的读者会发现,函数指针和数组指针的声明非常类似,只是将[]操作符换位()操作符。的确,由于()和[]操作符的优先级都高于*,所以指向数组的指针和指向函数的指针声明都很类似。
不过,目前[]和()操作符还没有一起出现过,如果一起出现,会是什么情况?
e) int (*var[6])();
第一层:(*var[6]),[]优先级高于*,所以var先与[]操作符做计算,var是一个数组。接下来对数组的元素做解引用,那么数组的元素是指针。
第二层:int xxx(),很容易吧,数组的元素解引用后是一个函数,函数的返回值是int。
结论:var是一个数组,有6个元素,元素的类型是指向函数的指针,函数的参数是void,返回值是int。
其实看破指针的声明,分辨指针、数组、函数并不困难,只要你牢记本文反复强调的两点:
1) 牢记*, [], ()操作符的优先级
2) 从变量名开始,由内向外的顺序分层分析
再复杂的声明都能清晰地解释。
而且,变量是指针、数组还是函数,在一层中就能确定。
1) 如果变量第一层的形式是(*var),那么var一定是一个指针;
2) 如果变量第一层的形式是(...var[x]),那么var一定是一个数组;
3) 如果变量第一层的形式是(...var()),那么var一定是一个函数;
至于指针指向的是什么数据类型,数组的元素是什么数据类型,函数的参数和返回值是什么类型,则需要继续向外层看才能判断。
耐心读完本章的读者,恭喜你,相信你现在已经对指针的声明有了很多的了解,并且能够区别指针、数组、函数的声明。虽然指针的声明还能更复杂,甚至远远复杂,但是在实际情况中并不会出现。本章的内容相信能够涵盖实际中的95%的情况,理解本章已经足够让你在实际中使用指针了。更重要的是,如果你能理解本章提出的图示法,相信即使遇到再复杂的指针声明,你都能独立去解析。
当然,关于指针的讨论远远没有结束,指针的声明只是第一步,还有更多的复杂问题。比如,什么是指向数组的指针?“指向数组的指针”和“指向数组第一个元素的指针”有什么差别?请见下一章,“指针+1”。
以上是关于C语言指针系列——并不可怕的声明的主要内容,如果未能解决你的问题,请参考以下文章