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.2.6 C51的数组指针 - 图1

图 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.2.6 C51的数组指针 - 图2

图 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)//元素的运算