hello,大家好,鸡啄米上一讲讲了数组的声明和使用,今天给大家讲下数组的存储与初始化、对象数组和数组作为函数参数的知识。
一.数组的存储
数组在内存中是一组连续的内存单元,也就是说数组元素是连续存储的。数组名是数组所占内存的首地址。
一维数组是按照下标的顺序存储的,而对多维数组就复杂些,以一定的约定顺序将多维数组存储在连续的内存单元中很重要。因为要对数组赋初值、函数间的数组数据传递等都需要先知道数组元素和存储位置的对应关系。
一维数组的元素是按照下标从小到大的顺序存在内存中的,例如,int a[3]在内存中的存储顺序是:a[0] a[1] a[2]。
对于二维数组元素,第一个下标叫做行标,第二个下标叫做列标。例如,数组int a[2][3]相当于一个两行三列的矩阵:
a[0][0] a[0][1] a[0][2]
a[1][0] a[1][1] a[1][2]
上面的a[0][0],行标为0,列标也为0,表示矩阵第一行第一个元素,a[1][2],行标为1,列标为2,表示矩阵第二行第三个元素。二维数组在内存中是按行存储的,也就是先存第一行,再存第二行...。每行中的元素按照列标从小到大的顺序存储。这种存储方式叫做行优先存储。上面例子中的二维数组a在内存中的存储顺序是:a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]。
对于多维数组,其存储方式与二维数组类似。可以把多维数组的下标看作一个计数器。多维数组右边的下标为低位,左边的为高位,每一位都在各自的上界与下界之间变化。当某一位超过上界则向左进一位,该位及右边各位就恢复为下界。最右边一维的下标变化最快,最左边的变化最慢。鸡啄米再提醒大家注意,下界都是0,某一维的上界是声明时该维的下标表达式值减1。例如,数组int a[2][2][2]在内存中的存储顺序是:a[0][0][0] a[0][0][1] a[0][1][0] a[0][1][1] a[1][0][0] a[1][0][1] a[1][1][0] a[1][1][1]。
实际上我们在软件开发时三维及三维以上的数组很少用到,用的最多的是一维数组。
二.数组的初始化
在我们声明数组时对部分或全部元素赋值就是数组的初始化。这里鸡啄米分开给大家讲简单数组类型的数组和对象数组,简单数组类型数组的初始化就是给数组元素赋初值,而对象数组的每个元素都是某个类的对象,它的初始化就是调用对象的构造函数。对象数组下面单独讲。
我们可以在声明数组时就给出每个元素的初值,例如:int a[2] = { 2,3 };这条语句声明了一个有2个元素的int类型的数组,a[0]的初值是2,a[1]的初值是3。
如果我们在声明数组时每个元素都给出初始值的话,就可以不说明元素个数,例如:int a[] = { 2,3 };和上面的那个数组声明语句等价。
我们也可以只对数组的前面一部分元素赋初值,例如,int a[3] = { 1,2 };这时数组元素的个数就必须给出,经过这样声明后,a[0]为1,a[1]为2,a[3]默认为0,也就是后面没有赋初值的元素默认为0。
多维数组的初始化也满足上面讲到的规则。另外,如果我们对数组初始化时给出了全部元素的初值,则第一位的元素个数可以不用显式说明,例如:int a[2][2] = { 1,2,1,2 };等价于int a[][2] = { 1,2,1,2 };。多维数组还可以按照第一维的下标分组,用括号把每一组括起来。二维数组的话可以用大括号将每组括起来,例如:int a[2][2] = { {1,2},{1,2}};与上面的语句等价,通过将每组元素括起来我们更能直观的分开每行数据。
三.对象数组
当数组的元素是某个类的对象时此数组就是对象数组。声明一维对象数组的形式为:类名 数组名[下标表达式];。跟前面说过的基本数据类型的数组一样,使用对象数组也只能引用单个的数组元素,而每个数组元素都是对象,利用这个对象又可以引用它的公有成员,引用形式为:数组名[下标].成员名。
对象数组在初始化时每个对象元素都会调用其构造函数。如果初始化时数组元素显式给出初始值就会调用带形参的构造函数,如果没有显式指定初始值则调用默认构造函数。例如,A b[2] = { A(2,3) };会先调用带形参的构造函数初始化b[0],再调用默认构造函数初始化b[1]。
四.数组作为函数参数
函数的参数可以是数组元素也可以是数组名。数组元素作为函数参数时跟同类型的变量作函数参数效果一样。
数组名作为函数参数时,实参和形参都须是数组名,并且数组类型要一样。此时传递的是数组的首地址,也就是说形参数组的首地址跟实参是一样的,后面的元素根据其在内存中的顺序进行对应,对应的元素的内存地址相同,所以实参数组的元素个数应该等于或多于形参数组的元素个数。如果在函数内对数组元素值改变,则主调函数中实参数组的相应元素也会改变。
鸡啄米给大家一个数组作为函数参数的例子:主函数中初始化一个矩阵并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。
#include <iostream> using namespace std; void RowSum(int A[][4], int nrow) { int sum; for (int i = 0; i < nrow; i++) { sum = 0; for(int j = 0; j < 4; j++) sum += A[i][j]; cout << "Sum of row " << i << " is " << sum << endl; A[i][0]=sum; } } int main() { int Table[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}}; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) cout << Table[i][j] << " "; cout << endl; } RowSum(Table,3); for (int i = 0; i < 3; i++) cout << Table[i][0] << endl; return 0; }
此程序的运行结果是:
1 2 3 4
2 3 4 5
3 4 5 6
Sum of row 0 is 10
Sum of row 1 is 14
Sum of row 2 is 18
10
14
18
鸡啄米对数组的存储与初始化、对象数组和数组作为函数参数的内容就讲到这里,如果有什么问题可以在鸡啄米博客上留言讨论。谢谢大家的支持。