0. 引言
在学习二维数组的时候,会自然产生以下几个问题,但是介于目前没有对以下几个问题有详细的讲解,那今天我来对下面几个自然提出的问题做一下详解:
- 二维数组如何进行声明?
- 二维数组如何作为指针传递到函数形参中?
- 二维数组如何向函数传递某一行的元素?
- 在使用二维数组的时候我们有哪些注意事项,如何节省系统的存储空间?
在我们正式开始之前,建议大家可以先回顾以下我们一维数组的相关知识。
1. 二维数组的声明
在此处,我们不阐述究竟二维数组其存储方式及表示意义,我们所述的是二维数组的用法。
那么二维数组究竟该如何声明呢?笔者为你整理了一下:
- 我们可以直接声明二维数组的row和column,并以初始化。(值得注意的是,用{}赋值仅仅只能在初始化阶段使用)
int a[4][5]={{0,0,0,0,0},
{1,1,1,1,1},
{2,2,2,2,2},
{3,3,3,3,3}};
- 或者我们也可以声明后不对其进行初始化定义:
int a[4][5];
- 在更不确定的情况下,我们可以省略row的赋值:
int a[][5];
但是必须提醒的是,省略column的值是不对的!
2. 指向二维数组的指针
我们如何声明一个指针变量p指向我们上述的二维数组a呢?
在此之前,我们需要详细讲解一下极易混淆的两句话:
int (*p)[5];
int *p[4];
Command 1: int (*p)[5];
先看第一句,我们知道,下标引用的优先级要高于间接引用,即[]的优先级要高于*,但是由于括号的存在,首先执行的还是间接访问。所以p是一个指针,但它指向什么呢?
接下来执行的是下标引用,所以p指向某种类型的数组。
虽然声明并没有直接告诉你p是什么,但是推他它的类型并不困难----当我们对他执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值,所以p是一个指向整型数组的指针。
在声明中加上初始化后是下面这个样子:
int a[4][5];
int (*p)[5] = a; //一定要理解,5是二维数组a的column
它使p指向a的第一行。
p是一个指向拥有5个整型元素的指针。不妨我们实际运行一下程序,看下程序运行会出现什么样的结果:
#include <stdio.h>
void main()
{
int a[4][5]={{0,0,0,0,0},
{1,1,1,1,1},
{2,2,2,2,2},
{3,3,3,3,3}};
int (*p)[5];
p = a;
}
我们利用CLion的监控变量,看看这个时候p到底代表了什么:
显然,我们验证了这句话:p是一个指向拥有5个整型元素的指针
而当你把p与一个整数相加时,该整数值首先根据5个整型值的长度进行调整,然后再执行加法。所以我们可以使用这个指针一行一行地在a中进行移动,比如:
#include <stdio.h>
void main()
{
int a[4][5]={{0,0,0,0,0},
{1,1,1,1,1},
{2,2,2,2,2},
{3,3,3,3,3}};
int (*p)[5];
p = a;
p++;
}
这个时候,我们可以通过监控变量看到:
当p进行加1运算的时候,p这个指针则指向了第二组5个整型数据。
可是如果我们需要一个指针逐个访问整型元素而不是逐行在数组中移动,我们应该怎么办呢?
int *p = &a[0][0];
放入整个代码中即为:
#include <stdio.h>
void main()
{
int a[4][5]={{0,0,0,0,0},
{1,1,1,1,1},
{2,2,2,2,2},
{3,3,3,3,3}};
int *p = &a[0][0];
}
这个时候,p则指向了一个将二维数组揉成一维数组的一个整型数组,此时进行运算:
p += 19;
printf("结果是%d",*p);
这个时候输出为“结果是3”。
或者我们也可将int *p = &a[0][0];
替代为int *p = a[0];
,具有相同的效果。
Command 2: int *p[4];
那这个第二句是什么意思呢?
由于[]优先级高,先与p结合,所以p成为一个内含4个元素的数组。然后*表示p数组内含4个指针。最后,int表示p数组中的指针都指向int类型的值。因此,这行代码声明了两个指向int的指针。而前面有圆括号的版本,*先与pz结合,因此声明的是一个指向数组(内含5个int类型的值)的指针。
我们试一下运行下面的代码:
#include "stdio.h"
void main()
{
const int a[4][5]={{0,0,0,0,0},{1,1,1,1,1},{2,2,2,2,2},{3,3,3,3,3}};
int *p[4];
for(int i=0;i<5;i++)
{
p[i] = a[i];
}
printf("The first number is %d.\n", a[0][0]);
}
从结果我们可以显然看到,p首先是含有4个元素的数组,而这4个元素都是指向整型的指针,最终每个元素都是指向a的每行首个元素的指针。
3. 将指针传递到函数的形参中
a. 将整个二维数组传递到函数的形参中
这样的函数的原型声明方式有几种呢?
void func1 ( int (*p)[5] );
void func2 ( int a[ ][5] );
void func3 ( int a[4][5] );
b. 将二维数组的某一行传递到函数的形参中
为了方便我们讲解,不妨我们先定义一个自定义函数:
void function(int *a,int *b)
{
int i;
for(i=0;i<5;i++)
{
printf("a[%d]=%d ",i,a[i]);
}
for(i=0;i<5;i++)
{
printf("b[%d]=%d ",i,b[i]);
}
}
我们依然以上文所述的两种定义情况来做讲解:
Command 1: int (*p)[5];
我们总结一下上文所说,这个时候p是指向二维数组a的第一行的五个元素的数组指针,所以要将a的第一、二行传递到函数中,我们应该用以下语句:
function(p,q+1);
运行结果是:a[0]=0 a[1]=0 a[2]=0 a[3]=0 a[4]=0 b[0]=1 b[1]=1 b[2]=1 b[3]=1 b[4]=1
Command 2: int *p[4];
我们总结一下上文所述,这个时候p是指向二维数组a的每一行的首元素的数组指针,所以要将a的第一、二行传递到函数中,我们应该用以下语句:
function(p[0],p[1]);
运行结果是:a[0]=0 a[1]=0 a[2]=0 a[3]=0 a[4]=0 b[0]=1 b[1]=1 b[2]=1 b[3]=1 b[4]=1
4. 总结
现在再回头看我们开头所提出的几个问题:
- 二维数组如何进行声明?
- 二维数组如何作为指针传递到函数形参中?
- 二维数组如何向函数传递某一行的元素?
- 在使用二维数组的时候我们有哪些注意事项,如何节省系统的存储空间?
我相信大家一定都颇有收获了。
本文所参考例子是本人经过较长时间思考所得出非常经典的例子,希望大家仔细思考吸收!
参考文献
[1] Kenneth R. Pointers on C[M]. Pearson Education India, 2007.
[2] Prata S. C primer plus[M]. Pearson Education, 2014.