------------------------------------------------我是首当其冲的分割线---------------------------------------------------
注解:严格地说,C++中没有多维数组,通常所指的多维数组其实就是数组的数组:
//array of size 3,each element is an array of ints of size 4
int ia[3][4];
在使用多维数组时,记住这一点有利于理解其应用.
----------------------------------------------我分割线首当其冲-----------------------------------------------------------
如果 数组的元素又是数组,则称为二维数组,其每一维对应一个下标:
ia[2][3] // fetches last element from the array in the last row
第一维通常称为行(row),第二维则称为列(column).C++中并未限制可用的下标个数,也就是说,我们可以定义元素是数组(其元素又是数组,如此类推)的数组.
1.多维数组的初始化
和处理一维数组一样,程序员可以使用由花括号括起来的初始化式列表来初始化多维数组的元素.对于多维数组的每一行,可以再用花括号指定其元素的初始化式:
int ia[3][4]={ /* 3 elements,each element is an array of size 4*/
{0,1,2,3} /* initializers for row indexed by 0 */
{4,5,6,7} /* initializers for row indexed by 1 */
{8,9,10,11} /* initializers for row indexed by 2 */
}
其中用来标志每一行的内嵌的花括号是可选的.下面的初始化尽管有点不清楚,但与前面的声明完全邓加:
// equivalent initialization without the optional nested braces for each row
int ia[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
与一维数组一样,有些元素将不使用初始化列表提供的初始化式进行初始化.下面的声明只初始化了每行的第一个元素:
//explicitly initialize nly element 0 in each row
int ia[3][4]={{0},{4},{8}};
其余元素根据其元素类型用通常的规则初始化.
如果省略内嵌的花括号,结果会完全不同:
//explicitly initialize row 0
int ia[3][4]={0,3,6,9};
该声明初始化了第一行的元素,其余元素都被初始化为0.
2.多维数组的下标引用
为了对多维数组进行索引,每一维都需要一个下标.例如,下面的嵌套for循环初始化了一个二维数组:
const size_t rowSize=3;
const size_t colSize=4;
int ia[rowSize][colSize]; //12 uninitialized elements
//for each row
for(size_t i=0;i!=rowSize;++i)
//for each column within the row
for(size_t j=0;j!=colSize;++j)
//initialize to its positional index
ia[i][j]=i*colSize+j;
当需要访问数组中的特定元素时,必须提供其行下标和列下标.行下标指出需要哪个内部数组,列下标则选取该内部数组的指定元素.了解多维数组下标引用策略有助于正确计算其下标值,以及理解多维数组如何初始化.
如果表达式只提供了一个下标,则结果获取的元素是该行下标索引的内层数组.如ia[2]将获得ia数组的最后一行,即这一行的内层数组本身,而并非该数组中的任何元素.
指针和多维数组
与普通数组一样,使用多维数组的指针时,实际上将其自动转换为指向该数组第一个元素的指针.
定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组
因为多维数组其实就是数组的数组,所以由多维数组转换而成的指针类型应该是指向第一个内层数组的指针.尽管这个概念非常明了,但声明这种指针的语法还是不容易理解:
int ia[3][4]; //array of size 3,each element is an array of ints of size 4
int (*ip)[4]=ia; //ip points an array of 4 ints
ip=&ia[2]; //ia[2] is an array of 4 ints
定义指向数组的指针与如何定义数组本身类似:首先声明元素类型,后接(数组)变量名字和维数.窍门在于(数组)变量的名字其实是指针,因此需在标识符前加上*.如果从内往外阅读ip的声明,则可理解为:*ip是int[4]类型----------即ip是一个指向含有4个元素的数组的指针
-----------------------------------------------我是疲倦的分割线-------------------------------------------------
在下面的生命中,圆括号是必不可少的:
int *ip[4]; //array of pointers to int
int(*ip)[4]; //pointer to an array of 4 ints
----------------------------------------------我是疲倦的分割线---------------------------------------------------
用typedef简化指向多维数组的指针
typedef类型定义可使指向多维数组元素的指针更容易读、写和理解.以下程序用typedef为ia的元素类型定义新的类型名:
typedef int int_array[4];
int_array *ip=ia;
可使用typedef类型输出ia的元素:
for(int_array *p=ia;p!=ia+3; ++p)
for(int *q=*p;q!=*p+4;++q)
cout<<*q<<endl;
外层的for循环首先初始化p指向ia的第一个内部数组,然后一直循环到ia的三行数据都处理完为止.++p使p加1,等效于移动指针使其指向ia的下一行(例如:下一个元素).
内层的for循环实际上处理的是存储在内部数组中的int型元素值,首先让q指向p所指向的数组的第一个元素.对p进行解引用获得一个有4个int型元素的数组,通常,使用这个数组时,系统会自动将它转换为指向该数组第一个元素的指针.在本例中,第一个元素是int型数据,q指向这个整数.系统执行内层的for循环知道处理完当前p指向的内部数组中所有的元素为止,当q指针刚达到该内部数组的超出末端位置时,再次对p进行解引用以获得指向下一个内部数组第一个元素的指针.在p指向的地址上加4使得系统可循环处理每一个内部数组的4个元素
习题4.36 重写程序输出ia数组的内容,要求在外层循环中不能使用typedef定义的类型.
#include<iostream>
using namespace std;
int main()
{
int ia[3][4]={//三个元素,每个元素是一个有四个int元素的数组
{0,1,2,3},//0行的初始化列表
{4,5,6,7},//1行的初始化列表
{8,9,10,11}//2行的初始化列表
};
int(*p)[4];
for(p=ia;p!=ia+3;++p)
for(int *q=*p;q!=*p+4;++q)
cout<<*q<<endl;
return 0;
}
比较上文
for(int_array *p=ia;p!=ia+3; ++p)
for(int *q=*p;q!=*p+4;++q)
cout<<*q<<endl;
体会体会