如果一个数组的每个元素都是指针变量,这个数组就是指针数组。指针数组的每个元素都必须是同一类型的指针。
1.一维指针数组
声明一维指针数组的语法形式:
数据类型*数组名[下标表达式];
下标表达式指出数组元素的个数,数据类型确定每个元素指针的类型,数组名是指针数组的名称,同时也是这个数组的首地址。例如,下列语句:
int *p[3];
声明了一个int类型的指针数组p,其中有3个元素,每个元素都是指向int类型数据的指针。
由于指针数组的每一个元素都是一个指针,必须先赋值在引用,因此声明数组后,对指针元素赋初值是必不可少的。
【例】利用指针数组输出一个3行3列单位矩阵
单位矩阵是主对角线元素为1,其余元素为0的矩阵。
int main()
{
int a[3] = { 1,0,0 };//定义数组,矩阵第一行
int b[3] = { 0,1,0 };//定义数组,矩阵第二行
int c[3] = { 0,0,1 };//定义数组,矩阵第三行
int* p[3] = { a,b,c };//定义整型指针数组并初始化
for (int i = 0; i < 3; i++)//对指针数组元素循环
{
for (int j = 0; j < 3; j++)//对矩阵每一行进行循环
{
cout << p[i][j]<<" ";
//cout << *(p[i]+j)<<" ";
}
cout << endl;
}
return 0;
}
运行结果:
结果分析:
在程序中,定义了3个元素的int型指针数组,并在定义的同时用a,b,c三个数组的首地址为这3个元素初始化,使每个元素分别指向矩阵的一行,矩阵的每一行都用一个数组存放,然后通过指针数组的元素访问存放矩阵数据的int型数组。
上述代码中的cout << p[i][j]<<" ";
与cout << *(p[i]+j)<<" ";
等价,即先把指针数组p所存储的第i个指针读出,然后读取它所指向的地址后方的第j个数。它的表示形式上与访问二维数组非常相似,但在具体的访问过程上却不大一样。
2.指针数组与二维数组的区别
二维数组在内存中是以行优先的方式按照一维顺序关系存放的。因此对于一个二维数组,可以理解为一维数组的一维数组,数组名是它的首地址,这个数组的元素个数就是行数,每一个元素是一个一维数组。例如,声明一个int型二维数组:
int arr[3][3]={{1,2,3},{2,3,4},{3,4,5}};
arr[0]是一个长度为3的一维数组,当arr[0]在表达式中出现时,表示一个指向该一维数组首地址的整型指针,这和一维数组的数组名指向该数组的首地址是一样的道理,所以可以用*(arr[0])
来表示arr[0][0],用*(arr[0]+1)
来表示arr[0][1]。上例中的p[i][j]
与这里的arr[i][j]
的不同之处在于,对于p来说,p[i]的值需要通过读取指针数组p的第i个元素才能得到,而arr[i]的值是通过二维数组arr的首地址计算得到的,内存中并没有一个指针数组来存储arr[i]的值。如下图所示:
尽管指针数组与二维数组存在本质上的差异,但二者具有相同的访问形式,可以把二维数组当作指针数组来访问。
【例】二维数组举例
int main()
{
int arr[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
cout << *(*(arr+i)+j) << " ";//逐个输出二维数组第i行的元素
}
cout << endl;
}
return 0;
}
运行结果:
通过数组元素的地址可以输出二维数组的元素:形式如下:
*(*(arr+i)+j)
这就是arr数组的第i行j列元素,对应使用下标表示的arr[i][j]
。