5.2.6 C51的数组指针
数组指针是指向数组的指针变量。数组在单片机内存中是连续存放的,数组名就是数组在内存中的首地址。数组指针指向数组,既可以指向数组的首地址,也可以指向数组元素的地址,即指针变量可以指向数组或数组元素。通过使用数组指针可以访问数组和数组元素。下面就根据所指向的数组类型的不同,分别介绍不同类型的数组指针。
1.指向一维数组的指针
定义一个指向一维数组的数组指针,要分别定义一个整型数组和一个指向整型变量的指针变量。示例如下。
int a[10];//定义数组
int*p;//定义指针变量
p=&a[0];//定义指针指向数组的首地址
本例中,p=&a[0]使指针变量p指向数组a中的第1个元素,即a[0]。数组的第1个元素的地址即数组的首地址。指针p指向a[0],即指针p指向数组a。在C51中,数组名也可表示数组的首地址,因此,可以采用下面的等价语句。
p=a;
通过数组指针及其相关运算可以间接访问数组中的任何一个元素。根据上一节介绍的指针变量的运算规则,可以用指针表示数组元素的地址和内容。
❑a+i和p+i都可以用来表示数组元素a[i]的地址,都指向数组第i个元素,即指向a[i]。
❑(p+i)和(a+i)都可以用来表示数组元素a[i]的内容,即p+i和a+i均指向数组元素a[i]的值。指针运算的程序,示例如下。
int a[10];//定义数组
int*p;//定义指针变量
p=a+5;//给指针变量赋初值
本例中,由于p初始值指向a[5],所以p[2]就相当于(p+2),表示a[7];而p[-2]就相当于(p-2),它表示a[3]。
在C51中,数组指针可以用带下标的方式表示。指向数组元素的指针可以表示成数组的形式,例如p[i]与*(p+i)等价。一维数组指针的程序示例如下。
include<stdio.h>//头文件
void main()
//主函数
{
int i;//定义整型变量
int a[10]={0,1,2,3,4,5,6,7,8,9};//定义并初始化整型数组a
int*p;//定义整型指针变量p
p=a;//整型指针变量p指向数组a
for(i=0;i<10;i++)//循环输出指针p所指向的内容
printf(“%d”,*(p+i));
printf(“\n”);//输出换行符
for(i=0;i<10;i++)//另一种方式,输出指针p所指向的内容
printf(“%d”,p[i]);
printf(“\n”);//输出换行符
}
该程序可以在KeilµVision3编译环境中执行,运行的结果如下。
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
本例中,先分别定义将整型数组a和整型指针变量p,再使数组指针p指向数组a,然后用for循环,分别使用指针运算和指针变量带下标的方式,输出p所指向地址中的内容,即数组a的各数组元素的值。
2.指向二维数组的指针
要定义一个指向二维数组的数组指针,需要首先定义一个二维数组,示例如下。
char a[3][4]={{‘a','b','c','d'},{'e','f','g','h'},{'i','j','k','l’}};
本例中,定义一个二维字符型数组并进行初始化赋值,该数组3行4列,共12个元素。在C51中,这个二维数组a可以看成是以3个一维数组a[0]、a[1]、a[2]为元素组成的数组。该二维数组的结构,如图5.7所示。
图 5.7 二维字符型数组a的结构
其中每个一维数组又包含有4个数组元素,分别表示如下。
❑a[0]所代表的一维数组包含4个元素:a[0][0]、a[0][1]、a[0][2]、a[0][3]。
❑a[1]所代表的一维数组包含4个元素:a[1][0]、a[1][1]、a[1][2]、a[1][3]。
❑a[2]所代表的一维数组包含4个元素:a[2][0]、a[2][1]、a[2][2]、a[2][3]。
在C51中,数组名a代表二维数组的首地址,也就是二维数组a[0]的首地址。按照指针变量的运算规则,a+1就代表a[1]的首地址,a+2就代表a[2]的首地址。程序示例如下。
include<stdio.h>//头文件
void main()//主函数
{
int a[3][4]={{1,1,3,3},{5,6,7,8},{9,10,1,2}};//定义并初始化二维整型数组ch
printf(“a=%d\n”,a);//输出a的值
printf(“a=%d\n”,a);//输出*a的值
}
该程序可以在KeilµVision3编译环境中执行,运行的结果如下。
a=1000
*a=1000
从本例可以看出,程序输出a和a的结果是相同的,都是1000。二维数组a可以看成是以3个一维数组a[0]、a[1]、a[2]为元素组成的数组。a表示数组的首地址,即a[0][0]的地址。a即表示a[0],a[0]是一维数组名,同时也是二维数组第0行第0列元素的地址,因此输出相同的地址值。
说明这段程序在不同的编译环境和硬件系统中输出的值不一定是1000,这是正常的,因为数组的地址是由系统自动分配的,但是a和*a的输出值必定是相同的。
假设二维字符型数组a在内存中存放的首地址为0x2000,由于每一行有4个单字节的字符型元素,所以a+1,即a[1]的首地址为0x2004,a+2即a[2]的首地址为0x2008,如图5.8所示。
图 5.8 二维字符型数组的地址
计算地址值时,要注意二维数组中的元素类型,对地址进行适当的放大。例如,定义数组a[3][4]为二维整型数组,此时每个数组元素需要占用两个字节的存储单元。此时,假如a的首地址为0x2000,则a[1]的地址为0x2008,a[2]的地址为0x2016。
在C51中,数组a的a[0]、a[1]、a[2]可以分别视为一维数组,其数组名分别代表所对应的一维数组的首地址,应满足以下的运算规则。
❑a[0]代表第0行第0列元素的地址,即&a[0][0];a[1]是第1行第0列元素的地址,即&a[1][0]。
❑一般来说,a[i]+j即代表第i行第j列元素的地址,即&a[i][j]。例如,a[0]+1即代表第0行第1列元素的地址,即&a[0][1]。
二维数组的数组名的功能类似于指针变量。因此,可用指针的形式来表示数组名,示例如下。
a[0]=&a[0][0]=*(a+0);
a[i]+j=&a[i][j]=*(a+i)+j;
在C51语言中,对于二维数组元素,以下的几种表示是等价的。
a[i][j]=((a+i)+j)=(a[i]+j)=((a+i))[j];
使用数组指针表示二维数组的地址和元素的方法既简便又灵活,但是在程序中使用时要注意理解其含义,否则容易出错。数组指针的程序示例如下。
include<stdio.h>//头文件
void main()//主函数
{
int a[3][4]={{1,1,3,3},{5,6,7,8},{9,10,1,2}};//定义并初始化二维整型数组a
int*pr;//定义整型指针变量
int i,j;//定义整型变量
for(i=0;i<3;i++)//循环输出数组a中的元素,使用a[i][j]
for(j=0;j<4;j++)
printf(“%d”,a[i][j]);
printf(“\n”);//输出换行符
for(i=0;i<3;i++)//循环输出a中的元素,使用((a+i)+j)
for(j=0;j<4;j++)
printf(“%d”,((a+i)+j));
printf(“\n”);//输出换行符
for(pr=a[0];pr<a[0]+12;p++)//循环输出a中的元素,使用指针pr作运算
printf(“%d”,*pr);
printf(“\n”);//输出换行符
}
本例中分别用等价的3种方法输出二维整型数组a中的各数组元素。该程序可以在KeilµVision3编译环境中执行,运行的结果如下。
1 1 3 3 5 6 7 8 9 10 1 2
1 1 3 3 5 6 7 8 9 10 1 2
1 1 3 3 5 6 7 8 9 10 1 2
3.指向一个由n个元素所组成的数组指针
在C51语言中,还可以直接定义一个数组指针指向由n个元素构成的数组,其定义格式如下。
类型标识符(*指针名)[n];
其中,类型标识符表示数组指针的类型,指针名即数组指针的变量名,示例如下。
int a[3][4];//定义二维数组a
int(*p)[4];//定义指针p
p=a;//指针赋值
其中,定义指针p是整型数组指针,指向一个由4个元素所组成的数组。当整型指针指向一个整型数组的元素时,若其进行指针加1运算,其地址值实际增加了8,即放大因子为2×4=8,相当于指向数组隔行的元素。例如,这里p=a[0][0],则下一个元素p[1]=ch[1][0]。
利用指向一个由n个元素所组成的数组指针,可以很方便地访问二维指针。其程序示例如下。
include<stdio.h>//头文件
void main()//主函数
{
int a[3][4]={{1,2,3,9},{5,6,7,8},{9,5,1,2}};//定义并初始化二维整型数组a
int(*p)[3];//定义由3个元素组成的数组指针
int i;//定义整型变量
p=a;//指针p指向数组a
for(i=0;i<3;i++)//循环输出*p[i]的值
printf(“%d”,*p[i]);
printf(“\n”);//换行符
for(i=0;i<3;i++)//循环输出*(p[i]+1)的值
printf(“%d”,*(p[i]+1));
printf(“\n”);//换行符
p=&a[0][2];//重新定义p的指向
for(i=0;i<3;i++)//循环输出*p[i]的值
printf(“%d”,*p[i]);
printf(“\n”);//换行符
}
该程序可以在KeilµVision3编译环境中执行,运行的结果如下。
1 5 9
2 6 5
3 7 1
本例中,定义了一个3行4列的二维整型数组a和一个指向3个元素的数组指针p,并使指针p指向数组a。使用p[0]、p[1]、p[2]输出数组a每行的第1个元素值。使用(p[0]+1)、(p[1]+1)、(p[2]+1)输出数组a每行第2个元素值。再重新定义p的指向,使p指向数组元素a[0][2],即第0行第3个元素,再使用p[0]、p[1]、*p[2]输出数组a每行的第3个元素值。
4.指针和数组的关系
在C51中,指针和数组关系密切,使用十分灵活。在程序中使用指针可使代码更灵活,也可以使程序执行得更快,并使生成的目标代码更小。任何能由数组和下标完成的操作,也完全可以由指针和指针的偏移量来实现。由于指针和数组的使用十分相似,很容易混淆,在这里列出指针和数组之间的一些典型关系,以帮助读者的理解。
❑对于定义一维数组及其指针变量,示例如下。
int a[4];
int*p;
p=a;
定义后的指向一维数组的数组指针,具有如下的几种操作。
a+i=p+i;(i=0,1,2,3)//地址的运算
a[i]=(a+i)=p[i]=(p+i);(i=0,1,2,3)//元素的运算
❑对于定义二维数组及其指针变量,示例如下。
int a[3][4];
int*p;
p=a;
定义后的指向二维数组的数组指针,具有如下的几种操作。
a=*a=a[0]=p;//地址的运算
a[i]=(a+i)=(p+i);(i=0,1,2)//地址的运算
&a[i][j]=a[i]+j=(a+i)+j=(p+i)+j;(i=0,1,2;j=0,1,2,3)//地址的运算
a[i][j]=(a[i]+j)=((a+i)+)=((a+i))[j]=((p+i)+j)=(*(p+i))[j];
(i=0,1,2;j=0,1,2,3)//元素的运算