6.3 指针与数组
指针用于存储地址,它和数组名紧密地联系在一起。图6-9就显示了一个数组名称为grade的一维整型数组,它包含5个数据,每个整型元素占用4个字节,内存中存储形式如图所示。
图6-9 数组在内存中存储形式
访问数组元素,可以使用下标来实现,比如要访问数组元素grade[3],在之前的章节里详细的讲解过,但是那时使用的方法其实隐藏了数组每一个元素的地址。根据之前讲过的地址的概念,结合数组在内存中占用一块连续的存储区域的特点可知,如果知道了数组某个元素的地址,改变偏移量,就可以得到数组其他元素的地址,进而访问该元素值。
以grade数组为例,数组元素grade[0]的地址可以通过取地址运算符“&”获得,即&grade[0],数组中grade[3]元素的地址计算方法如下所示:
&grade[3]=&grade[0]+3*4;
如果有指针变量p指向数组元素grade[0],根据指针变量自运算规则,使用指针访问grade[3]的代码如下所示:
指针变量p首先指向数组元素grade[0],正向偏移3个存储单元后,p指向数组元素grade[3]。
【例6.6】分别使用下标和指针访问数组元素。
代码如下:
运行结果如图6-10所示。
图6-10
分析:
表达式*(p+i)表达式的含义是,指针从数组首地址偏移i个存储单元(由于int数据占4个字节,所以每次偏移4*i个字节),访问该单元中的数据值。
需要注意的是*(p+i)表达式中的括号是必须有的,不能省略。如果遗漏了小括号,将变成这样的表达式*p+i,它的含义是对p所指向的存储单元的数值加i,而不是p+i所指向存储单元的值。
6.3.1 数组名作为指针
每个创建的一维数组,数组名就成为编译器为这个数组所创建的指针常量名称,它存储的是数组第一个元素的地址,也就是数组首地址。这样,就又多了一种获取数组元素地址的手段。
例如:
分析下面的代码,看看错了吗?
grade=grade+3;
错误的。原因是,grade是数组名,它是指针常量,常量是不能够赋值的。
再分析下面的代码,是否有问题?
p=grade;
p=p+3;
正确的。因为p是指针变量,变量的值是可以改变的,p的初值是grade数组的首地址,p=p+3表示p指向数组元素grade[3],此时访问*p就相当于访问数组元素grade[3]的值。
【例6.7】有n个整数,存储在数组array中,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数,并输出。
例如:输入数据“1 2 3 4 5 6”,输出数据“4 5 6 1 2 3”。
代码如下:
运行结果如图6-11所示。
图6-11
分析:
代码arrayend=*(array+n-1)表示存储数组最后一个元素的值,因为循环后移数组元素将覆盖原位置数据,导致最后一个数据元素丢失,故应先将其保存。
6.3.2 指针与多维数组
数组在内存中占用连续的存储区域,以二维数组array[2][3]为例,6个数组元素数在内存储中存储形式如图6-12所示。
图6-12 二维数组在内存中存储形式
下面逐步分级二维数组和指针的关系:
第一步,写出最容易理解的形式:
【例6.8】使用三种不同的方法访问二维数组。
代码如下:
运行结果如图6-13所示。
图6-13
分析:
第一个for循环使用下标形式直接访问了二维数组元素;第二个for循环使用了数组名间接访问了二维数组元素;第三个for循环使用指针访问了二维数组元素,因为p是一个指针变量,它的初值是数组的首地址,通过改变偏移量,使指针p指向不同地址,实现了输出数组元素的目的。
首先指针p指向数组array的首地址,就是array[0][0]的地址,因为数组array是由2行3列组成的,所以3*i+j表示第i行第j列元素对应的偏移量;*(p+3*i+j)表示的就是数组元素array[i][j]。
【例6.9】统计在array字符串中从a到z的26个小写字母各自出现的次数。
例如:输入字符串"abcdefgabcdeabc",输出的结果应该是“33322110000000000000000000”。
代码如下:
运行结果如图6-14所示。
图6-14
分析:
cnt函数头行使用字符指针*p接收字符数组s的首地址,函数cnt中使用代码p++改变指针p的指向,使用*p访问字符数组元素。题目很巧妙地使用差值*p-ˊaˊ作为数组元素的下标,代码arr[*p-ˊaˊ]++统计每个小写字母的个数。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。