Team LiB
Previous Section Next Section

4.1. Arrays

4.1. 数组

An array is a compound type (Section 2.5, p. 58) that consists of a type specifier, an identifier, and a dimension. The type specifier indicates what type the elements stored in the array will have. The dimension specifies how many elements the array will contain.

数组是由类型名、标识符和维数组成的复合数据类型(第 2.5 节),类型名规定了存放在数组中的元素的类型,而维数则指定数组中包含的元素个数。

The type specifier can denote a built-in data or class type. With the exception of references, the element type can also be any compound type. There are no arrays of references.

数组定义中的类型名可以是内置数据类型或类类型;除引用之外,数组元素的类型还可以是任意的复合类型。没有所有元素都是引用的数组。



4.1.1. Defining and Initializing Arrays

4.1.1. 数组的定义和初始化

The dimension must be a constant expression (Section 2.7, p. 62) whose value is greater than or equal to one. A constant expression is any expression that involves only integral literal constants, enumerators (Section 2.7, p. 62), or const objects of integral type that are themselves initialized from constant expressions. A nonconst variable, or a const variable whose value is not known until run time, cannot be used to specify the dimension of an array.

数组的维数必须用值大于等于1的常量表达式定义(第 2.7 节)。此常量表达式只能包含整型字面值常量、枚举常量(第 2.7 节)或者用常量表达式初始化的整型 const 对象。非 const 变量以及要到运行阶段才知道其值的 const 变量都不能用于定义数组的维数。

The dimension is specified inside a [] bracket pair:

数组的维数必须在一对方括号 [] 内指定:

          // both buf_size and max_files are const
          const unsigned buf_size = 512, max_files = 20;
          int staff_size = 27;            // nonconst
          const unsigned sz = get_size();  // const value not known until run time
          char input_buffer[buf_size];     // ok: const variable
          string fileTable[max_files + 1]; // ok: constant expression
          double salaries[staff_size];     // error: non const variable
          int test_scores[get_size()];     // error: non const expression
          int vals[sz];                    // error: size not known until run time

Although staff_size is initialized with a literal constant, staff_size itself is a nonconst object. Its value can be known only at run time, so it is illegal as an array dimension. Even though size is a const object, its value is not known until get_size is called at run time. Therefore, it may not be used as a dimension. On the other hand, the expression

虽然 staff_size 是用字面值常量进行初始化,但 staff_size 本身是一个非 const 对象,只有在运行时才能获得它的值,因此,使用该变量来定义数组维数是非法的。而对于 sz,尽管它是一个 const 对象,但它的值要到运行时调用 get_size 函数后才知道,因此,它也不能用于定义数组维数。

          max_files + 1

is a constant expression because max_files is a const variable. The expression can be and is evaluated at compile time to a value of 21.

另一方面,由于 max_filesconst 变量,因此表达式是常量表达式,编译时即可计算出该表达式的值为21。

Explicitly Initializing Array Elements
显式初始化数组元素

When we define an array, we can provide a comma-separated list of initializers for its elements. The initializer list must be enclosed in braces:

在定义数组时,可为其元素提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表:

          const unsigned array_size = 3;
          int ia[array_size] = {0, 1, 2};

If we do not supply element initializers, then the elements are initialized in the same way that variables are initialized (Section 2.3.4, p. 50).

如果没有显式提供元素初值,则数组元素会像普通变量一样初始化(第 2.3.4 节):

  • Elements of an array of built-in type defined outside the body of a function are initialized to zero.

    在函数体外定义的内置数组,其元素均初始化为 0。

  • Elements of an array of built-in type defined inside the body of a function are uninitialized.

    在函数体内定义的内置数组,其元素无初始化。

  • Regardless of where the array is defined, if it holds elements of a class type, then the elements are initialized by the default constructor for that class if it has one. If the class does not have a default constructor, then the elements must be explicitly initialized.

    不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化。

Unless we explicitly supply element initializers, the elements of a local array of built-in type are uninitialized. Using these elements for any purpose other than to assign a new value is undefined.

除非显式地提供元素初值,否则内置类型的局部数组的元素没有初始化。此时,除了给元素赋值外,其他使用这些元素的操作没有定义。

An explicitly initialized array need not specify a dimension value. The compiler will infer the array size from the number of elements listed:

显式初始化的数组不需要指定数组的维数值,编译器会根据列出的元素个数来确定数组的长度:

          int ia[] = {0, 1, 2}; // an array of dimension 3

If the dimension size is specified, the number of elements provided must not exceed that size. If the dimension size is greater than the number of listed elements, the initializers are used for the first elements. The remaining elements are initialized to zero if the elements are of built-in type or by running the default constructor if they are of class type:

如果指定了数组维数,那么初始化列表提供的元素个数不能超过维数值。如果维数大于列出的元素初值个数,则只初始化前面的数组元素;剩下的其他元素,若是内置类型则初始化为0,若是类类型则调用该类的默认构造函数进行初始化:

          const unsigned array_size = 5;
          // Equivalent to ia = {0, 1, 2, 0, 0}
          // ia[3] and ia[4] default initialized to 0
          int ia[array_size] = {0, 1, 2};
          // Equivalent to str_arr = {"hi", "bye", "", "", ""}
          // str_arr[2] through str_arr[4] default initialized to the empty string
          string str_arr[array_size] = {"hi", "bye"};
Character Arrays Are Special
特殊的字符数组

A character array can be initialized with either a list of comma-separated character literals enclosed in braces or a string literal. Note, however, that the two forms are not equivalent. Recall that a string literal (Section 2.2, p. 40) contains an additional terminating null character. When we create a character array from a string literal, the null is also inserted into the array:

字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。然而,要注意这两种初始化形式并不完全相同,字符串字面值(第 2.2 节)包含一个额外的空字符(null)用于结束字符串。当使用字符串字面值来初始化创建的新数组时,将在新数组中加入空字符:

          char ca1[] = {'C', '+', '+'};                // no null
          char ca2[] = {'C', '+', '+', '\0'};         // explicit null
          char ca3[] = "C++";     // null terminator added automatically

The dimension of ca1 is 3; the dimension of ca2 and ca3 is 4. It is important to remember the null-terminator when initializing an array of characters to a literal. For example, the following is a compile-time error:

ca1 的维数是 3,而 ca2ca3 的维数则是 4。使用一组字符字面值初始化字符数组时,一定要记得添加结束字符串的空字符。例如,下面的初始化将导致编译时的错误:

          const char ch3[6] = "Daniel"; // error: Daniel is 7 elements

While the literal contains only six explicit characters, the required array size is sevensix to hold the literal and one for the null.

上述字符串字面值包含了 6 个显式字符,存放该字符串的数组则必须有 7 个元素——6 个用于存储字符字面值,而 1 个用于存放空字符 null。

No Array Copy or Assignment
不允许数组直接复制和赋值

Unlike a vector, it is not possible to initialize an array as a copy of another array. Nor is it legal to assign one array to another:

与vector不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组,这些操作都是非法的:

          int ia[] = {0, 1, 2}; // ok: array of ints
          int ia2[](ia);        // error: cannot initialize one array with another

          int main()
          {
              const unsigned array_size = 3;
              int ia3[array_size]; // ok: but elements are uninitialized!

              ia3 = ia;           //  error: cannot assign one array to another
              return 0;
          }

Some compilers allow array assignment as a compiler extension. If you intend to run a given program on more than one compiler, it is usually a good idea to avoid using nonstandard compiler-specific features such as array assignment.

一些编译器允许将数组赋值作为编译器扩展。但是如果希望编写的程序能在不同的编译器上运行,则应该避免使用像数组赋值这类依赖于编译器的非标准功能。

Caution: Arrays Are Fixed Size

警告:数组的长度是固定的

Unlike the vector type, there is no push_back or other operation to add elements to the array. Once we define an array, we cannot add elements to it.

vector 类型不同,数组不提供 push_back 或者其他的操作在数组中添加新元素,数组一经定义,就不允许再添加新元素。

If we must add elements to the array, then we must manage the memory ourselves. We have to ask the system for new storage to hold the larger array and copy the existing elements into that new storage. We'll see how to do so in Section 4.3.1 (p. 134).

如果必须在数组中添加新元素,程序员就必须自己管理内存:要求系统重新分配一个新的内存空间用于存放更大的数组,然后把原数组的所有元素复制到新分配的内存空间中。我们将会在第 4.3.1 节学习如何去实现。

Exercises Section 4.1.1

Exercise 4.1:

Assuming get_size is a function that takes no arguments and returns an int value, which of the following definitions are illegal? Explain why.

假设 get_size 是一个没有参数并返回 int 值的函数,下列哪些定义是非法的?为什么?

          unsigned buf_size = 1024;

          (a) int ia[buf_size];
          (b) int ia[get_size()];
          (c) int ia[4 * 7 - 14];
          (d) char st[11] = "fundamental";

Exercise 4.2:

What are the values in the following arrays?

下列数组的值是什么?

          string sa[10];
          int ia[10];
          int main() {
              string sa2[10];
              int    ia2[10];
          }

Exercise 4.3:

Which, if any, of the following definitions are in error?

下列哪些定义是错误的?

          (a) int ia[7] = { 0, 1, 1, 2, 3, 5, 8 };
          (b) vector<int> ivec = { 0, 1, 1, 2, 3, 5, 8 };
          (c) int ia2[ ] = ia1;
          (d) int ia3[ ] = ivec;

Exercise 4.4:

How can you initialize some or all the elements of an array?

如何初始化数组的一部分或全部元素?

Exercise 4.5:

List some of the drawbacks of using an array instead of a vector.

列出使用数组而不是 vector 的缺点。


4.1.2. Operations on Arrays

4.1.2. 数组操作

Array elements, like vector elements, may be accessed using the subscript operator (Section 3.3.2, p. 94). Like the elements of a vector, the elements of an array are numbered beginning with 0. For an array of ten elements, the correct index values are 0 through 9, not 1 through 10.

与vector元素一样,数组元素可用下标操作符(第 3.3.2 节)来访问,数组元素也是从 0 开始计数。对于一个包含 10 个元素的数组,正确的下标值是从 0 到 9,而不是从 1 到 10。

When we subscript a vector, we use vector::size_type as the type for the index. When we subscript an array, the right type to use for the index is size_t (Section 3.5.2, p. 104).

在用下标访问元素时,vector 使用 vector::size_type 作为下标的类型,而数组下标的正确类型则是 size_t第 3.5.2 节)。

In the following example, a for loop steps through the 10 elements of an array, assigning to each the value of its index:

在下面的例子中,for 循环遍历数组的 10 个元素,并以其下标值作为各个元素的初始值:

          int main()
          {
              const size_t array_size = 10;
              int ia[array_size]; // 10 ints, elements are uninitialized

              // loop through array, assigning value of its index to each element
              for (size_t ix = 0; ix != array_size; ++ix)
                    ia[ix] = ix;
              return 0;
          }

Using a similar loop, we can copy one array into another:

使用类似的循环,可以实现把一个数组复制给另一个数组:

          int main()
          {
              const size_t array_size = 7;
              int ia1[] = { 0, 1, 2, 3, 4, 5, 6 };
              int ia2[array_size]; // local array, elements uninitialized

              // copy elements from ia1 into ia2
              for (size_t ix = 0; ix != array_size; ++ix)
                    ia2[ix] = ia1[ix];
              return 0;
          }

Checking Subscript Values
检查数组下标值

As with both strings and vectors, the programmer must guarantee that the subscript value is in rangethat the array has an element at the index value.

正如 stringvector 类型,程序员在使用数组时,也必须保证其下标值在正确范围之内,即数组在该下标位置应对应一个元素。

Nothing stops a programmer from stepping across an array boundary except attention to detail and thorough testing of the code. It is not inconceivable for a program to compile and execute and still be fatally wrong.

除了程序员自己注意细节,并彻底测试自己的程序之外,没有别的办法可防止数组越界。通过编译并执行的程序仍然存在致命的错误,这并不是不可能的。

By far, the most common causes of security problems are so-called "buffer overflow" bugs. These bugs occur when a subscript is not checked and reference is made to an element outside the bounds of an array or other similar data structure.

导致安全问题的最常见原因是所谓“缓冲区溢出(buffer overflow)”错误。当我们在编程时没有检查下标,并且引用了越出数组或其他类似数据结构边界的元素时,就会导致这类错误。



Exercises Section 4.1.2

Exercise 4.6:

This code fragment intends to assign the value of its index to each array element. It contains a number of indexing errors. Identify them.

下面的程序段企图将下标值赋给数组的每个元素,其中在下标操作上有一些错误,请指出这些错误。

          const size_t array_size = 10;
          int ia[array_size];
          for (size_t ix = 1; ix <= array_size; ++ix)
                ia[ix] = ix;

Exercise 4.7:

Write the code necessary to assign one array to another. Now, change the code to use vectors. How might you assign one vector to another?

编写必要的代码将一个数组赋给另一个数组,然后把这段代码改用 vector 实现。考虑如何将一个 vector 赋给另一个 vector

Exercise 4.8:

Write a program to compare two arrays for equality. Write a similar program to compare two vectors.

编写程序判断两个数组是否相等,然后编写一段类似的程序比较两个 vector

Exercise 4.9:

Write a program to define an array of 10 ints. Give each element the same value as its position in the array.

编写程序定义一个有 10 个 int 型元素的数组,并以其在数组中的位置作为各元素的初值。


Team LiB
Previous Section Next Section