【C语言更新】指向数组的指针
文/Edward
前面我们在讲述数组的时候曾经说过,对于数组内部元素的引用方式主要有两种,一种就是使用方括号“[ ]”的下标索引,另一种就是之前提到过,但是没有细致讲述的指针引用。
事实上,数组和指针这两个东西,很多人都会以为它们是一样的,其实不然,指针是指针,数组是数组,它们两个是完全不一样的,只不过我们可以轻易地用指针来引用数组中的某一个元素而已。
前面在定义数组的时候,我们就有介绍,数组定义的本质就是在内存上面开辟出一块固定长度的变量序列,然后将这一组的变量序列根据顺序编号,这些编号便是在下标索引时候的偏移量。
图1 数组内存排列
我们在介绍指针的时候也说过,指针的定义本质就是在内存上定义一个特殊的变量,这个变量特殊在哪里?特殊在与普通变量不同的是,这个指针变量存放的是一个地址。一旦一个指针指向某一个普通变量,那么这个变量的地址将会被存放在指针变量内部。
那么我们将指针和数组联系起来看一下,假设我们定义了一个无符号字符型的数组(每个元素占据一个内存最小单位,即字节,不存在地址奇偶对齐),接着,我们再定义一个无符号字符型的指针变量,将这个变量指向这个数组的第一个元素,那么我们想一下,当我取这个指针指向的变量时,是不是就是指向了这个数组的第一个元素?如果我们需要取第二个元素的时候怎么办?很简单只需要对这个指针本身加一,它就会跳到指向的下一个内存单元里面去,即指向了数组的下一个元素。
有意思的是,我们上面选用无符号字符型的数据类型,只是为了先不去解释指针加一的功能。事实上,指针定义时之所以需要指明其数据类型,就是由于指针自加时需要用到这个信息。指针每次加一时到底它会往下增长几个内存地址,这是指针的数据类型决定的。比如int类型的指针变量加一时,它正好向下增加四个字节,和int的数据长度一样。这样的好处就是,只要对指针本身加一,它就会和数组的索引保持一致。
经过上面的解释,利用指针去操作数组思路就变得很简单了,我们只需要定义一个指针,用它去指向一个数组,接着通过指针自身的增减就可以获取这个数组内部的任意一个元素了。如图2所示。
图2 指针指向数组
接下来我们需要弄清楚的一件事就很直观了,即如何使用一个指针指向一个数组。
第一种使用指针指向数组的方式是使用一个指针指向一个数组的第一个元素。我们来写一段代码测试一下,先定义一个数组,再去定义一个指针,接着用这个指针指向这个数组的第一个元素,即下标为0的元素,然后我们使用指针遍历去对这个数组当中索引为奇数的元素赋值0xff,最后遍历打印出这个数组。如图3所示。
图3 指针指向数组第一个元素
第二种方式则更简单,在C语言中,数组的名称其实就是这个数组的首地址,也就是这个数组第一个元素的地址,这两者是等价的。因此我们只需要用指针指向这个数组名即可实现指向数组的目的了,同样的程序修改为图4所示。
图4 指针指向数组名
以上是用指针指向数组的内容,C语言支持指针指向数组,并不是仅仅为了像操作数组一样去操作指向数组的指针的,其中最重要的一个目的是为了传递函数的结果。我们之前说了,一个函数只能有一个返回值,这些返回值可以是整形,字符型或者空类型,最多只能有一个返回值,这就决定了如果有一个函数对一个数组进行运算,需要返回一个数组时,无法通过函数返回值的形式将结果传递出来。唯一的办法就是使用数组指针,将结果返回出来。
比如,我们需要实现一个功能函数,将一个数组的前n个字节拷贝到另一个数组中去。要实现这个功能,如果单单使用函数的返回值传递结果,那么无论如何都是传递不出来的,唯一的办法就是使用指向数组的指针作为这个函数的形式参数,接着使用形式参数的方法将结果传递出来。如,拷贝的目标数组为dest,源数组为src,拷贝的长度为length,那么我们可以写出如图5所示的函数。
图5 指向数组的指针传递函数结果