距离我学习C语言也有四年之久了,我不禁想到,自己是否真的了解和会完C语言指针,以下是我对C语言指针的理解(如有纰漏,请指出
我做了codewars
上的 C Puzzle: Pointer Monster
,其中运用到了一些关于指针相关的内容,我感觉这是一道学习指针和数组和函数之间的较好的一个例子
特别是不用编译器去算指针,数组,偏移后所得出来了的最后的答案,我觉得对理解数组和指针帮助还是有一定的帮助的
学过计算机组成原理就知道,哦,这个是个间接寻址~
我们可以使用 *x
去访问 这块地址的值!
我们知道,内存被分为了一系列连续的小块,对于每一个小块,我们给它们取了个编号
这样,我们就有了我们的地址
这个概念
那么,我们的指针是指向一个地址的,它可以支持加减操作,每次操作对应着移动到下一个块去
那么,我们是不是可以使用 *(x + 1)
来访问 下一个地址的值呢?
那么数组是个什么东西呢?
首先,数组给的内存肯定是先分配好的,不然会段错误。
其次,我眼中的数组其实就是一个 int * const
(这个指针是不能够被改变的
当然,无论是指针还是数组,他都可以写成一个乖乖的形式:
大概是因为1[t] 在编译时会被翻译为 1+t 吧~
二重指针指向的是一个int*
那么我们的指针就应该长成 int* *
同理,我们的二维数组呢?
二重数组的地址分布是什么样呢?
那么,指向数组的指针呢?
注意,这是指向数组的指针,是一个指针,元素是数组
int (*)[k]
(k代表数组长度,得指定,数组和指针还是有区别的)
那么,指向指针的数组呢?
注意,这是指向指针的数组,是一个数组,元素是指针
看到这里,可能开始模糊起来了,可能就读不清楚是个什么类型了
简单过一下螺旋法则吧~
*
=> 指向(类型待定)的指针
arr
这个需要被判定的对象出发。
[
左方括号 ->arr
是一个尺寸为 7 的数组。
*
-> 所以 arr
是一个尺寸为 7 的数组,元素为指针。
int
-> 所以 arr
是一个尺寸为 7 的数组,数组元素: int
类型的指针。
下面自己分析一个复杂的吧~
接下来,可能要变得有趣起来了
因为指针和数组,一重时,最多两种情况,而二维时,我们则有4中情况了
三重理论上排列出来的组合还是蛮多的,可能有这么多种,但实际不一定,这里就介绍四种
第一种是 指向 数组的指针 的指针
第二种是 声明 数组的指针 的数组
第三种是 指向 指针的数组 的指针
第四种是 声明 指针的数组 的数组
只要按照这样套娃,100层我都套的出来,不过那样的代码味道一定蛮大的~
可以看出C语言中的这种类型属实不好认。。。
让我们的心智负担降低了不少
那么,上续说的很简单,可是我们遗漏掉了一个东西,函数
我们的函数声明长这个样儿
我们的函数指针类型长什么样子呢?
简单,我们把指针塞到变量的位置嘛!
还是比较好理解的,用螺旋法则。
现在,让我们来升级一下我们的函数指针
现在我们要定义如下东西:
声明 函数指针的 数组
记住,记住,螺旋法则看待,我们先是一个数组
用的时候,大概长这个样子
指向 函数指针的 指针
回想我们刚才,采用枚举的方式,构造了一系列复杂的指针和数组
通过枚举的方式,对于函数指针也是一样的可行道理
懒得写了,太多了,枚举不过来
现在,我们来写一个比较复杂的函数,就用我们刚才的那几个组成
现在函数样式已经定下来了
当然,上述声明放到编译器中去编译是不会通过的
tips : 貌似C语言中并不能返回一个数组,可以返回值
那么,现在我们考虑如何把函数写出来
我们若是要定义一个变量如何定义?
那么,现在换做我的函数呢?
我们可以等价的替换掉,即:
把他看作一个变量名一样放进去
现在我们再给参数加上变量名,就可以得到我们的函数
那么,我们的函数指针呢?
替换的规律,我们把fun替换为一个 (*)
就可以办到这一件事情
好,现在写出来了,如何,还是比较复杂吧
当然,还要更复杂的,我可以返回一个函数数组指针,参数也是函数数组指针,还要指针数组和数组指针
数组和指针是非常灵活的东西,我们的C/C++程序员,尽可能用好指针
最好使用typedef和using来简化我们的操作
现代语言,比如golang、rust这种现在都已经采用后置类型的方式了,就我而已,我任务后置类型是比c/c++这种形式要来的好的
且可以保持统一的风格:
当然,C++也可以做到一点保持统一风格
但不可忘记这些东西,举个例子:
你不理解函数指针,你就搞不懂 c++ 中的 function
是如何实现的,有没有其他实现方式
C++的类型萃取更是离不开类型的分辨
甚至你可以使用 模板来给类型添加指针这样的操作
模板再编译期是可以执行的,所以基本上没有损耗一般
就很容易的达到了 指向 数组指针的 指针
最后,附上我开始说的那道题的代码,感受一下吧~