Team LiB
Previous Section Next Section

4.4. Multidimensioned Arrays

4.4. 多维数组

Strictly speaking, there are no multidimensioned arrays in C++. What is commonly referred to as a multidimensioned array is actually an array of arrays:

严格地说,C++ 中没有多维数组,通常所指的多维数组其实就是数组的数组:


          // array of size 3, each element is an array of ints of size 4
          int ia[3][4];

It can be helpful to keep this fact in mind when using what appears to be a multidimensioned array.

在使用多维数组时,记住这一点有利于理解其应用。


An array whose elements are an array is said to have two dimensions. Each dimension is referred to by its own subscript:

如果数组的元素又是数组,则称为二维数组,其每一维对应一个下标:

     ia[2][3] // fetches last element from the array in the last row

The first dimension is often referred to as the row and the second as the column. In C++ there is no limit on how many subscripts are used. That is, we could have an array whose elements are arrays of elements that are arrays, and so on.

第一维通常称为行(row),第二维则称为列(column)。C++ 中并未限制可用的下标个数,也就是说,我们可以定义元素是数组(其元素又是数组,如此类推)的数组。

Initializing the Elements of a Multidimensioned Array

多维数组的初始化

As with any array, we can initialize the elements by providing a bracketed list of initializers. Multidimensioned arrays may be initialized by specifying bracketed values for each row:

和处理一维数组一样,程序员可以使用由花括号括起来的初始化式列表来初始化多维数组的元素。对于多维数组的每一行,可以再用花括号指定其元素的初始化式:

     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 */
     };

The nested braces, which indicate the intended row, are optional. The following initialization is equivalent, although considerably less clear.

其中用来标志每一行的内嵌的花括号是可选的。下面的初始化尽管有点不清楚,但与前面的声明完全等价:

     // 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};

As is the case for single-dimension arrays, elements may be left out of the initializer list. We could initialize only the first element of each row as follows:

与一维数组一样,有些元素将不使用初始化列表提供的初始化式进行初始化。下面的声明只初始化了每行的第一个元素:

     // explicitly initialize only element 0 in each row
     int ia[3][4] = {{ 0 } , { 4 } , { 8 } };

The values of the remaining elements depend on the element type and follow the rules descibed on page 112.

其余元素根据其元素类型用第 4.1.1 节描述的规则初始化。

If the nested braces were omitted, the results would be very different:

如果省略内嵌的花括号,结果会完全不同:

     // explicitly initialize row 0
     int ia[3][4] = {0, 3, 6, 9};

initializes the elements of the first row. The remaining elements are initialized to 0.

该声明初始化了第一行的元素,其余元素都被初始化为 0。

Subscripting a Multidimensioned Array

多维数组的下标引用

Indexing a multidimensioned array requires a subscript for each dimension. As an example, the following pair of nested for loops initializes a two-dimensioned array:

为了对多维数组进行索引,每一维都需要一个下标。例如,下面的嵌套 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;

When we want to access a particular element of the array, we must supply both a row and column index. The row index specifies which of the inner arrays we intend to access. The column index selects an element from that inner array. Remembering this fact can help in calculating proper subscript values and in understanding how multidimensioned arrays are initialized.

当需要访问数组中的特定元素时,必须提供其行下标和列下标。行下标指出需要哪个内部数组,列下标则选取该内部数组的指定元素。了解多维数组下标引用策略有助于正确计算其下标值,以及理解多维数组如何初始化。

If an expression provides only a single index, then the result is the inner-array element at that row index. Thus, ia[2] fetches the array that is the last row in ia. It does not fetch any element from that array; it fetches the array itself.

如果表达式只提供了一个下标,则结果获取的元素是该行下标索引的内层数组。如 ia[2] 将获得ia 数组的最后一行,即这一行的内层数组本身,而并非该数组中的任何元素。

4.4.1. Pointers and Multidimensioned Arrays

4.4.1. 指针和多维数组

As with any array, when we use the name of a multidimensioned array, it is automatically converted to a pointer to the first element in the array.

与普通数组一样,使用多维数组名时,实际上将其自动转换为指向该数组第一个元素的指针。

When defining a pointer to a multidimensioned array, it is essential to remember that what we refer to as a multidimensioned array is really an array of arrays.

定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组。

Because a multidimensioned array is really an array of arrays, the pointer type to which the array converts is a pointer to the first inner array. Although conceptually straightforward, the syntax for declaring such a pointer can be confusing:

因为多维数组其实就是数组的数组,所以由多维数组转换而成的指针类型应是指向第一个内层数组的指针。尽管这个概念非常明了,但声明这种指针的语法还是不容易理解:

     int ia[3][4];      // array of size 3, each element is an array of ints of size 4
     int (*ip)[4] = ia; // ip points to an array of 4 ints
     ip = &ia[2];       // ia[2] is an array of 4 ints

We define a pointer to an array similarly to how we would define the array itself: We start by declaring the element type followed by a name and a dimension. The trick is that the name is a pointer, so we must prepend * to the name. We can read the definition of ip from the inside out as saying that *ip has type int[4] that is, ip is a pointer to an int array of four elements.

定义指向数组的指针与如何定义数组本身类似:首先声明元素类型,后接(数组)变量名字和维数。窍门在于(数组)变量的名字其实是指针,因此需在标识符前加上 *。如果从内向外阅读 ip 的声明,则可理解为:*ipint[4] 类型——即 ip 是一个指向含有 4 个元素的数组的指针。

The parentheses in this declaration are essential:

在下面的声明中,圆括号是必不可少的:

     int *ip[4]; // array of pointers to int
     int (*ip)[4]; // pointer to an array of 4 ints
Typedefs Simplify Pointers to Multidimensioned Arrays
用 typedef 简化指向多维数组的指针

Typedefs (Section 2.6, p. 61) can help make pointers to elements in multidimensioned arrays easier to write, read, and understand. We might write a typedef for the element type of ia as

typedef 类型定义(第 2.6 节)可使指向多维数组元素的指针更容易读、写和理解。以下程序用 typedef 为 ia 的元素类型定义新的类型名:

     typedef int int_array[4];
     int_array *ip = ia;

We might use this typedef to print the elements of ia:

可使用 typedef 类型输出 ia 的元素:

     for (int_array *p = ia; p != ia + 3; ++p)
         for (int *q = *p; q != *p + 4; ++q)
              cout << *q << endl;

The outer for loop starts by initializing p to point to the first array in ia. That loop continues until we've processed all three rows in ia. The increment, ++p, has the effect of moving p to point to the next row (e.g., the next element) in ia.

外层的 for 循环首先初始化 p 指向 ia 的第一个内部数组,然后一直循环到 ia 的三行数据都处理完为止。++p 使 p 加 1,等效于移动指针使其指向 ia 的下一行(例如:下一个元素)。

The inner for loop actually fetches the int values stored in the inner arrays. It starts by making q point to the first element in the array to which p points. When we dereference p, we get an array of four ints. As usual, when we use an array, it is converted automatically to a pointer to its first element. In this case, that first element is an int, and we point q at that int. The inner for loop runs until we've processed every element in the inner array. To obtain a pointer just off the end of the inner array, we again dereference p to get a pointer to the first element in that array. We then add 4 to that pointer to process the four elements in each inner array.

内层的 for 循环实际上处理的是存储在内部数组中的 int 型元素值。首先让 q 指向 p 所指向的数组的第一个元素。对 p 进行解引用获得一个有 4 个 int 型元素的数组,通常,使用这个数组时,系统会自动将它转换为指向该数组第一个元素的指针。在本例中,第一个元素是int型数据,q指向这个整数。系统执行内层的 for 循环直到处理完当前 p 指向的内部数组中所有的元素为止。当 q 指针刚达到该内部数组的超出末端位置时,再次对 p 进行解引用以获得指向下一个内部数组第一个元素的指针。在 p 指向的地址上加 4 使得系统可循环处理每一个内部数组的 4 个元素。

Exercises Section 4.4.1

Exercise 4.36:

Rewrite the program to print the contents of the array ia without using a typedef for the type of the pointer in the outer loop.

重写程序输出 ia 数组的内容,要求在外层循环中不能使用 typedef 定义的类型。


Team LiB
Previous Section Next Section