C++语言指南(十一)——数组

2023-09-17 14:24
  C++语言指南(十一)——数组
*********************************************************
       原文:http://www.nigeriaembassy.cn/doc/tutorial/
*********************************************************
数组
一个数组是放在内存相邻区中的一系列类型相同的元素,它们能够通过把索引加到一个唯一的标识符来单独的引用。
那就意味着,例如,我们可以在一个数组中储存5int型的值而不用定义5个不同的拥有不同标识符变量。此外,通过使用数组我们能够使用一个单独的标识符来存储5个相同类型的不同的值,例如 int
例如,一个叫做billy的包含5个整型值的int型的数组可以像这样来表示:
其中每个空白处代表数组的一个元素,在这里是是int型的整型变量。这些元素被从04的分别来计数,这是因为在数组中第一个索引总是0
和一个通常的变量一样,一个数组必须在它使用前声明。在C++中对一个数组的典型声明为:
type name [elements];
其中type是一个有效的类型(如 int float……),name是一个有效的标识符,elements域(就是通常被括在方括号[]中的那部分),指名这个数组需要包含多少个这些元素。
因此,要声明一个像上图中给出的那样的一个叫billy的数组就是如此的简单:
int billy [5];
注意:在方括号[]中的表示这个数组将要包含的元素的数目的elements域,必须是一个常量,因为数组是非动态内存块,这些“非动态内存”块的大小必须在运行之前确定。为了创建长度可变的数组需要用到“动态内存”,“动态内存”将在这个指南的稍后介绍。
初始化数组.
当声明一个拥有局部作用域(例如在一个函数中)的普通数组时,如果我们没有作特殊的处理,它的元素将不会被缺省的初始化为任何值,因此在我们向其中存一些值之前,它们的内容是不确定的。全局的和静态数组则不同,它们被自动的被通过填充0来初始化。
在两种情况,局部和全局,当我们声明一个数组,我们可以通过括在花括号{}中的值来为数组的每个元素设置初始值。例如:
int billy [5] = { 16, 2, 77, 40, 12071 };
这个声明会创建这样的一个数组:
花括号{}间值的总数必须不能比我们在方括号[]中为数组声明的元素的个数多。例如,在数组billy的例子中我们声明其有5个元素,在花括号{}中的初始化值列表中我们指定了5个值,每一个对应一个元素。
当一个数组有初始化值的时候,C++允许左括号[]间是空的。在这种情况情况下,编译器将会为数组指定一个和花括号{}间的值的个数相匹配大小:
int billy [] = { 16, 2, 77, 40, 12071 };
在这个声明之后,数组billy将有5int的长度,因为我们提供了5个初始化变量。
访问数组中的值.
在程序中一个数组可见的任何地方,我们能够像访问一个普通变量一样单独的访问它的每一元素,这既可以读也可以修改它的值。格式就像下面所示一样简单:
name[index]
继续使用前面那个有5个元素、每个元素都是int型的billy作例子,我们可以用来关联每一个元素的名字如下:
例如,在billy的第三个元素处储存值75,我们可以写下面的语句:
billy[2] = 75;
并且,例如,把billy的第三个元素的值传递给变量a,我们可以写:
a = billy[2];
因此,表达式billy[2]从任何目的来说都像一个int型的变量。
注意billy的第三个元素被指定为billy[2],因为第一个是billy[0],第二个是billy[1],因此第三个是billy[2]。同样的原因,它的最后的元素是billy[4]。因此,如果我们写billy[5],我们将访问billy的第6个元素,而这超过了这个数组大小。
C++中超过一个数组的有效索引范围是语法正确的。这就带来了问题,因为访问范围外(out-of-range)的元素不会引起编译错误但会引起运行时错误。为什么这是被允许的原因将会在后面我们开始使用指针时看到。
在这里弄清楚和数组有关的方括号[]的两种不同的用法是很重要的。它们完成不同的任务:一个是在数组被声明时指明数组的大小;第二个是指明具体数组元素的索引。不要弄混在数组中这两个方括号[]的可能用途。
int billy[5];         // declaration of a new Array
billy[2] = 75;        // access to an element of the Array.
如果你仔细的读,你会发现一个类型说明符总是在一个变量或数组的声明之前,而它却从不在一个访问之前。
一些其它的对数组有效的操作:
billy[0] = a;
billy[a] = 75;
b = billy [a+2];
billy[billy[a]] = billy[2] + 5;
/
/ arrays example
#include
using namespace std;
 
int billy [] = {16, 2, 77, 40, 12071};
int n, result=0;
 
int main ()
{
 for ( n=0 ; n<5 ; n++ )
 {
    result += billy[n];
 }
 cout << result;
 return 0;
}
12206
多维数组
多维数组可以用“数组的数组”来形容。例如,一个二维数组可以被看作是由元素组成的一个二维表,所有元素都是相同的统一的数据类型。
jimmy表示一个有3行、每行有5个int类型元素的二维数组。在C++中声明这个数组的方法可以是:
int jimmy [3][5];
并且,例如,在一个表达式中引用第二行第四列的元素的代码可以是:
jimmy[1][3]
(记住数组的索引总是从0开始)。
多维数组不仅限为2个索引(例如,四维)。它们可以包含所需要多的索引。但是请小心!一个数组所需要的内存的总合随着每一维高速的增长。例如:
char century [100][365][24][60][60];
为一个世纪的每一秒声明一个char型数组,那将是多于30亿个字符。因此这个声明会消耗多于3G的内存!
多维数组只是对于程序员来说的一个抽象,因为我们可以通过在一个简单数组的索引间放一个系数而得到相同的结果:
int jimmy [3][5];   // is equivalent to
int jimmy [15];     // (3 * 5 = 15)
唯一的区别就是:对于多维数组编译器要记住我们假象的每一个维的深度。用这两个有完全一致结果的两段代码作为例子。一个使用二维数组,而另一个使用简单数组:
multidimensional array
pseudo-multidimensional array
#define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT][WIDTH];
int n,m;
int main ()
{
 for (n=0;n    for (m=0;m    {
      jimmy[n][m]=(n+1)*(m+1);
    }
 return 0;
}
#define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT * WIDTH];
int n,m;
int main ()
{
 for (n=0;n    for (m=0;m    {
      jimmy[n*WIDTH+m]=(n+1)*(m+1);
    }
 return 0;
}
上面的两段源代码多没有在屏幕上产生任何输出,但都用下面的方式为一个叫做jimmy的内存块赋值:
我们使用了“定义常量”(#define)来简化将来可能的对程序的修改。例如,当我们决定把数组的高从3增大到4,就可以简单改变行:
#define HEIGHT 3
到:
#define HEIGHT 4
而不需要对程序的其它修改。
数组做参数.
在有些时候我们可能需要把一个数组作为一个参数传个一个函数。在C++中把一整块内存通过“传值”的方式作为一个参数传给函数是不可能的,但是我们允许传它的地址。在实际中这将有相同的效果,而且它是更快、更高效的操作。
为了接受数组作为一个参数,我们在声明一个函数时需要作的唯一一件事就是在它的形参中指明数组中元素的类型,一个标示符合一对空的方括号[]。例如下面的函数:
void procedure (int arg[])
接受一个叫做argint型数组作为形参。为了把一个如下定义的数组传给这个函数:
int myarray [40];
像这样写一个调用就足够了:
procedure (myarray);
这里是一个完整的例子:
// arrays as parameters
#include
using namespace std;
 
void printarray (int arg[], int length) {
 for (int n=0; n
    cout << arg[n] << " ";
 cout << "/n";
}
 
int main ()
{
 int firstarray[] = {5, 10, 15};
 int secondarray[] = {2, 4, 6, 8, 10};
 printarray (firstarray,3);
 printarray (secondarray,5);
 return 0;
}
5 10 15
2 4 6 8 10
就像你看到的,第一个参数(int arg[])接受任何元素类型为int的数组,而不管它的长度。由于这个原因我们需要引入第二个参数来告诉函数,我们传给它的作为它第一个参数的数组的长度。这就使for循环知道传来的数组的迭代范围,而不会在输出数组时发生数组越界。
在一个函数声明中也可以包含多维数组。以一个三维数组作为参数的格式是:
Ibase_type[][depth][depth]
例如,一个带有多维数组作为参数的函数可以是:
void procedure (int myarray[][3][4])
注意:第一个方括号[]是空的,而它后面的却不是。只是因为编译器在函数的内部确定每一个增加的维的深度。
数组,一维或多维,对于一个程序员新手来说作为函数的参数传递都是一个非常共同的错误来源。我建议阅读关于“指针”的那一章,来更好的理解对数组是如何工作的。