如何在 C++ 中使用数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4810664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:36:36  来源:igfitidea点击:

How do I use arrays in C++?

c++arrayspointersmultidimensional-arrayc++-faq

提问by fredoverflow

C++ inherited arrays from C where they are used virtually everywhere. C++ provides abstractions that are easier to use and less error-prone (std::vector<T>since C++98 and std::array<T, n>since C++11), so the need for arrays does not arise quite as often as it does in C. However, when you read legacy code or interact with a library written in C, you should have a firm grasp on how arrays work.

C++ 从 C 继承了数组,它们几乎无处不在。C++ 提供了更易于使用且不易出错的抽象(std::vector<T>自 C++98 和std::array<T, n>C++11 起),因此对数组的需求不像在 C 中那样频繁出现。但是,当您阅读遗留文件时代码或与用 C 编写的库交互,您应该牢牢掌握数组的工作原理。

This FAQ is split into five parts:

本FAQ分为五个部分:

  1. arrays on the type level and accessing elements
  2. array creation and initialization
  3. assignment and parameter passing
  4. multidimensional arrays and arrays of pointers
  5. common pitfalls when using arrays
  1. 类型级别的数组和访问元素
  2. 数组创建和初始化
  3. 赋值和参数传递
  4. 多维数组和指针数组
  5. 使用数组时的常见陷阱

If you feel something important is missing in this FAQ, write an answer and link it here as an additional part.

如果您觉得此常见问题解答中缺少某些重要内容,请写下答案并将其作为附加部分链接到此处。

In the following text, "array" means "C array", not the class template std::array. Basic knowledge of the C declarator syntax is assumed. Note that the manual usage of newand deleteas demonstrated below is extremely dangerous in the face of exceptions, but that is the topic of another FAQ.

在下面的文本中,“数组”表示“C 数组”,而不是类模板std::array。假设具备 C 声明符语法的基本知识。请注意,下面演示的new和的手动使用delete在遇到异常时非常危险,但这是另一个常见问题解答的主题。

(Note: This is meant to be an entry to Stack Overflow's C++ FAQ. If you want to critique the idea of providing an FAQ in this form, then the posting on meta that started all thiswould be the place to do that. Answers to that question are monitored in the C++ chatroom, where the FAQ idea started out in the first place, so your answer is very likely to get read by those who came up with the idea.)

(注意:这是Stack Overflow 的 C++ FAQ 的一个条目。如果你想批评以这种形式提供 FAQ 的想法,那么开始所有这一切的 meta 上的帖子将是这样做的地方。答案该问题在C++ 聊天室中受到监控,FAQ 的想法首先在这里开始,因此您的答案很可能会被提出该想法的人阅读。)

采纳答案by fredoverflow

Arrays on the type level

类型级别的数组

An array type is denoted as T[n]where Tis the element typeand nis a positive size, the number of elements in the array. The array type is a product type of the element type and the size. If one or both of those ingredients differ, you get a distinct type:

数组类型被表示为T[n]其中T元素类型n是正大小,阵列中元件的数量。数组类型是元素类型和大小的乘积类型。如果这些成分中的一种或两种不同,您将获得不同的类型:

#include <type_traits>

static_assert(!std::is_same<int[8], float[8]>::value, "distinct element type");
static_assert(!std::is_same<int[8],   int[9]>::value, "distinct size");

Note that the size is part of the type, that is, array types of different size are incompatible types that have absolutely nothing to do with each other. sizeof(T[n])is equivalent to n * sizeof(T).

请注意,大小是类型的一部分,即不同大小的数组类型是不兼容的类型,彼此之间绝对没有任何关系。sizeof(T[n])相当于n * sizeof(T)

Array-to-pointer decay

数组到指针衰减

The only "connection" between T[n]and T[m]is that both types can implicitly be convertedto T*, and the result of this conversion is a pointer to the first element of the array. That is, anywhere a T*is required, you can provide a T[n], and the compiler will silently provide that pointer:

T[n]和之间的唯一“联系”T[m]是两种类型都可以隐式转换T*,并且这种转换的结果是指向数组第一个元素的指针。也就是说,在任何T*需要a 的地方,您都可以提供 a T[n],编译器将默默地提供该指针:

                  +---+---+---+---+---+---+---+---+
the_actual_array: |   |   |   |   |   |   |   |   |   int[8]
                  +---+---+---+---+---+---+---+---+
                    ^
                    |
                    |
                    |
                    |  pointer_to_the_first_element   int*

This conversion is known as "array-to-pointer decay", and it is a major source of confusion. The size of the array is lost in this process, since it is no longer part of the type (T*). Pro: Forgetting the size of an array on the type level allows a pointer to point to the first element of an array of anysize. Con: Given a pointer to the first (or any other) element of an array, there is no way to detect how large that array is or where exactly the pointer points to relative to the bounds of the array. Pointers are extremely stupid.

这种转换被称为“数组到指针衰减”,它是一个主要的混淆源。数组的大小在此过程中丢失,因为它不再是类型 ( T*) 的一部分。优点:在类型级别忘记数组的大小允许指针指向任何大小的数组的第一个元素。缺点:给定一个指向数组第一个(或任何其他)元素的指针,无法检测该数组有多大或指针相对于数组边界所指向的确切位置。指针极其愚蠢

Arrays are not pointers

数组不是指针

The compiler will silently generate a pointer to the first element of an array whenever it is deemed useful, that is, whenever an operation would fail on an array but succeed on a pointer. This conversion from array to pointer is trivial, since the resulting pointer valueis simply the address of the array. Note that the pointer is notstored as part of the array itself (or anywhere else in memory). An array is not a pointer.

只要认为有用,编译器就会默默地生成一个指向数组第一个元素的指针,也就是说,每当一个操作在数组上失败但在一个指针上成功时。这种从数组到指针的转换是微不足道的,因为结果指针只是数组的地址。请注意,指针作为数组本身(或内存中的任何其他位置)的一部分存储。数组不是指针。

static_assert(!std::is_same<int[8], int*>::value, "an array is not a pointer");

One important context in which an array does notdecay into a pointer to its first element is when the &operator is applied to it. In that case, the &operator yields a pointer to the entirearray, not just a pointer to its first element. Although in that case the values(the addresses) are the same, a pointer to the first element of an array and a pointer to the entire array are completely distinct types:

在其中一个阵列并一个重要方面当衰变到一个指向它的第一个元素是&操作者施加于它。在这种情况下,&运算符会产生一个指向整个数组的指针,而不仅仅是指向其第一个元素的指针。尽管在这种情况下(地址)相同,但指向数组第一个元素的指针和指向整个数组的指针是完全不同的类型:

static_assert(!std::is_same<int*, int(*)[8]>::value, "distinct element type");

The following ASCII art explains this distinction:

以下 ASCII 艺术解释了这种区别:

      +-----------------------------------+
      | +---+---+---+---+---+---+---+---+ |
+---> | |   |   |   |   |   |   |   |   | | int[8]
|     | +---+---+---+---+---+---+---+---+ |
|     +---^-------------------------------+
|         |
|         |
|         |
|         |  pointer_to_the_first_element   int*
|
|  pointer_to_the_entire_array              int(*)[8]

Note how the pointer to the first element only points to a single integer (depicted as a small box), whereas the pointer to the entire array points to an array of 8 integers (depicted as a large box).

注意第一个元素的指针是如何只指向一个整数(描绘成一个小盒子),而指向整个数组的指针指向一个由 8 个整数组成的数组(描绘成一个大盒子)。

The same situation arises in classes and is maybe more obvious. A pointer to an object and a pointer to its first data member have the same value(the same address), yet they are completely distinct types.

同样的情况也出现在课堂上,而且可能更加明显。指向对象的指针和指向其第一个数据成员的指针具有相同的(相同的地址),但它们是完全不同的类型。

If you are unfamiliar with the C declarator syntax, the parenthesis in the type int(*)[8]are essential:

如果您不熟悉 C 声明符语法,则类型int(*)[8]中的括号是必不可少的:

  • int(*)[8]is a pointer to an array of 8 integers.
  • int*[8]is an array of 8 pointers, each element of type int*.
  • int(*)[8]是一个指向 8 个整数数组的指针。
  • int*[8]是一个包含 8 个指针的数组,每个元素的类型为int*

Accessing elements

访问元素

C++ provides two syntactic variations to access individual elements of an array. Neither of them is superior to the other, and you should familiarize yourself with both.

C++ 提供了两种语法变体来访问数组的各个元素。它们都不优于另一个,您应该熟悉两者。

Pointer arithmetic

指针运算

Given a pointer pto the first element of an array, the expression p+iyields a pointer to the i-th element of the array. By dereferencing that pointer afterwards, one can access individual elements:

给定一个指向p数组第一个元素的指针,该表达式p+i产生一个指向数组第 i 个元素的指针。通过之后取消引用该指针,可以访问单个元素:

std::cout << *(x+3) << ", " << *(x+7) << std::endl;

If xdenotes an array, then array-to-pointer decay will kick in, because adding an array and an integer is meaningless (there is no plus operation on arrays), but adding a pointer and an integer makes sense:

如果x表示一个数组,那么数组到指针的衰减就会开始,因为添加一个数组和一个整数是没有意义的(对数组没有加号操作),但是添加一个指针和一个整数是有意义的:

   +---+---+---+---+---+---+---+---+
x: |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
     |           |               |
x+0  |      x+3  |          x+7  |     int*

(Note that the implicitly generated pointer has no name, so I wrote x+0in order to identify it.)

(注意隐式生成的指针是没有名字的,所以我x+0是为了标识而写的。)

If, on the other hand, xdenotes a pointerto the first (or any other) element of an array, then array-to-pointer decay is not necessary, because the pointer on which iis going to be added already exists:

另一方面,如果x表示指向数组第一个(或任何其他)元素的指针,则不需要数组到指针衰减,因为i要添加的指针已经存在:

   +---+---+---+---+---+---+---+---+
   |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
   +-|-+         |               |
x: | | |    x+3  |          x+7  |     int*
   +---+

Note that in the depicted case, xis a pointer variable(discernible by the small box next to x), but it could just as well be the result of a function returning a pointer (or any other expression of type T*).

请注意,在所描述的情况下,x是一个指针变量(可通过 旁边的小框识别x),但它也可能是返回指针的函数(或任何其他类型的表达式T*)的结果。

Indexing operator

索引运算符

Since the syntax *(x+i)is a bit clumsy, C++ provides the alternative syntax x[i]:

由于语法*(x+i)有点笨拙,C++ 提供了替代语法x[i]

std::cout << x[3] << ", " << x[7] << std::endl;

Due to the fact that addition is commutative, the following code does exactly the same:

由于加法是可交换的,因此以下代码完全相同:

std::cout << 3[x] << ", " << 7[x] << std::endl;

The definition of the indexing operator leads to the following interesting equivalence:

索引运算符的定义导致以下有趣的等价:

&x[i]  ==  &*(x+i)  ==  x+i

However, &x[0]is generally notequivalent to x. The former is a pointer, the latter an array. Only when the context triggers array-to-pointer decay can xand &x[0]be used interchangeably. For example:

但是,&x[0]一般等价于x。前者是指针,后者是数组。只有当上下文触发数组到指针衰减时x,才&x[0]可以互换使用。例如:

T* p = &array[0];  // rewritten as &*(array+0), decay happens due to the addition
T* q = array;      // decay happens due to the assignment

On the first line, the compiler detects an assignment from a pointer to a pointer, which trivially succeeds. On the second line, it detects an assignment from an arrayto a pointer. Since this is meaningless (but pointerto pointer assignment makes sense), array-to-pointer decay kicks in as usual.

在第一行,编译器检测到从一个指针到一个指针的赋值,这很容易成功。在第二行,它检测从数组到指针的赋值。由于这是无意义的(但指向指针赋值的指针是有意义的),数组到指针的衰减照常开始。

Ranges

范围

An array of type T[n]has nelements, indexed from 0to n-1; there is no element n. And yet, to support half-open ranges (where the beginning is inclusiveand the end is exclusive), C++ allows the computation of a pointer to the (non-existent) n-th element, but it is illegal to dereference that pointer:

类型数组T[n]具有n元素,索引从0to n-1; 没有元素n。然而,为了支持半开范围(其中开头是包含的,结尾是不包含的),C++ 允许计算指向(不存在的)第 n 个元素的指针,但取消引用该指针是非法的:

   +---+---+---+---+---+---+---+---+....
x: |   |   |   |   |   |   |   |   |   .   int[8]
   +---+---+---+---+---+---+---+---+....
     ^                               ^
     |                               |
     |                               |
     |                               |
x+0  |                          x+8  |     int*

For example, if you want to sort an array, both of the following would work equally well:

例如,如果您想对数组进行排序,以下两种方法同样有效:

std::sort(x + 0, x + n);
std::sort(&x[0], &x[0] + n);

Note that it is illegal to provide &x[n]as the second argument since this is equivalent to &*(x+n), and the sub-expression *(x+n)technically invokes undefined behaviorin C++ (but not in C99).

请注意,&x[n]作为第二个参数提供是非法的,因为这等效于&*(x+n),并且子表达式在*(x+n)技术上调用C++ 中的未定义行为(但不是在 C99 中)。

Also note that you could simply provide xas the first argument. That is a little too terse for my taste, and it also makes template argument deduction a bit harder for the compiler, because in that case the first argument is an array but the second argument is a pointer. (Again, array-to-pointer decay kicks in.)

另请注意,您可以简单地提供x作为第一个参数。这对我来说有点太简洁了,而且它也使编译器的模板参数推导有点困难,因为在这种情况下,第一个参数是一个数组,而第二个参数是一个指针。(再次,数组到指针的衰减开始了。)

回答by fredoverflow

Programmers often confuse multidimensional arrays with arrays of pointers.

程序员经常将多维数组与指针数组混淆。

Multidimensional arrays

多维数组

Most programmers are familiar with named multidimensional arrays, but many are unaware of the fact that multidimensional array can also be created anonymously. Multidimensional arrays are often referred to as "arrays of arrays" or "truemultidimensional arrays".

大多数程序员都熟悉命名多维数组,但很多人不知道多维数组也可以匿名创建。多维数组通常被称为“数组数组”或“真正的多维数组”。

Named multidimensional arrays

命名多维数组

When using named multidimensional arrays, alldimensions must be known at compile time:

使用命名多维数组时,必须在编译时知道所有维度:

int H = read_int();
int W = read_int();

int connect_four[6][7];   // okay

int connect_four[H][7];   // ISO C++ forbids variable length array
int connect_four[6][W];   // ISO C++ forbids variable length array
int connect_four[H][W];   // ISO C++ forbids variable length array

This is how a named multidimensional array looks like in memory:

这是命名多维数组在内存中的样子:

              +---+---+---+---+---+---+---+
connect_four: |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+

Note that 2D grids such as the above are merely helpful visualizations. From the point of view of C++, memory is a "flat" sequence of bytes. The elements of a multidimensional array are stored in row-major order. That is, connect_four[0][6]and connect_four[1][0]are neighbors in memory. In fact, connect_four[0][7]and connect_four[1][0]denote the same element! This means that you can take multi-dimensional arrays and treat them as large, one-dimensional arrays:

请注意,上面的 2D 网格只是有用的可视化。从 C++ 的角度来看,内存是一个“扁平”的字节序列。多维数组的元素按行优先顺序存储。也就是说,connect_four[0][6]并且connect_four[1][0]是内存中的邻居。其实connect_four[0][7]connect_four[1][0]表示同一个元素!这意味着您可以将多维数组视为大的一维数组:

int* p = &connect_four[0][0];
int* q = p + 42;
some_int_sequence_algorithm(p, q);

Anonymous multidimensional arrays

匿名多维数组

With anonymous multidimensional arrays, all dimensions except the firstmust be known at compile time:

对于匿名多维数组,除了第一个维度之外的所有维度必须在编译时已知:

int (*p)[7] = new int[6][7];   // okay
int (*p)[7] = new int[H][7];   // okay

int (*p)[W] = new int[6][W];   // ISO C++ forbids variable length array
int (*p)[W] = new int[H][W];   // ISO C++ forbids variable length array

This is how an anonymous multidimensional array looks like in memory:

这是匿名多维数组在内存中的样子:

              +---+---+---+---+---+---+---+
        +---> |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |
      +-|-+
   p: | | |
      +---+

Note that the array itself is still allocated as a single block in memory.

请注意,数组本身仍作为内存中的单个块分配。

Arrays of pointers

指针数组

You can overcome the restriction of fixed width by introducing another level of indirection.

您可以通过引入另一个间接级别来克服固定宽度的限制。

Named arrays of pointers

命名的指针数组

Here is a named array of five pointers which are initialized with anonymous arrays of different lengths:

这是一个由五个指针组成的命名数组,这些指针使用不同长度的匿名数组进行初始化:

int* triangle[5];
for (int i = 0; i < 5; ++i)
{
    triangle[i] = new int[5 - i];
}

// ...

for (int i = 0; i < 5; ++i)
{
    delete[] triangle[i];
}

And here is how it looks like in memory:

这是它在内存中的样子:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
triangle: | | | | | | | | | | |
          +---+---+---+---+---+

Since each line is allocated individually now, viewing 2D arrays as 1D arrays does not work anymore.

由于现在每行都是单独分配的,因此不再将二维数组视为一维数组。

Anonymous arrays of pointers

匿名指针数组

Here is an anonymous array of 5 (or any other number of) pointers which are initialized with anonymous arrays of different lengths:

这是一个由 5 个(或任何其他数量的)指针组成的匿名数组,这些指针使用不同长度的匿名数组进行初始化:

int n = calculate_five();   // or any other number
int** p = new int*[n];
for (int i = 0; i < n; ++i)
{
    p[i] = new int[n - i];
}

// ...

for (int i = 0; i < n; ++i)
{
    delete[] p[i];
}
delete[] p;   // note the extra delete[] !

And here is how it looks like in memory:

这是它在内存中的样子:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
          | | | | | | | | | | |
          +---+---+---+---+---+
            ^
            |
            |
          +-|-+
       p: | | |
          +---+

Conversions

转化次数

Array-to-pointer decay naturally extends to arrays of arrays and arrays of pointers:

数组到指针的衰减自然扩展到数组数组和指针数组:

int array_of_arrays[6][7];
int (*pointer_to_array)[7] = array_of_arrays;

int* array_of_pointers[6];
int** pointer_to_pointer = array_of_pointers;

However, there is no implicit conversion from T[h][w]to T**. If such an implicit conversion did exist, the result would be a pointer to the first element of an array of hpointers to T(each pointing to the first element of a line in the original 2D array), but that pointer array does not exist anywhere in memory yet. If you want such a conversion, you must create and fill the required pointer array manually:

但是,没有从T[h][w]到 的隐式转换T**。如果确实存在这种隐式转换,则结果将是指向指针数组的第一个元素的h指针T(每个指向原始二维数组中一行的第一个元素),但该指针数组不存在于记忆呢。如果你想要这样的转换,你必须手动创建和填充所需的指针数组:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = connect_four[i];
}

// ...

delete[] p;

Note that this generates a view of the original multidimensional array. If you need a copy instead, you must create extra arrays and copy the data yourself:

请注意,这会生成原始多维数组的视图。如果您需要一个副本,则必须创建额外的数组并自己复制数据:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = new int[7];
    std::copy(connect_four[i], connect_four[i + 1], p[i]);
}

// ...

for (int i = 0; i < 6; ++i)
{
    delete[] p[i];
}
delete[] p;

回答by fredoverflow

Assignment

任务

For no particular reason, arrays cannot be assigned to one another. Use std::copyinstead:

没有特别的原因,数组不能相互分配。使用std::copy来代替:

#include <algorithm>

// ...

int a[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int b[8];
std::copy(a + 0, a + 8, b);

This is more flexible than what true array assignment could provide because it is possible to copy slices of larger arrays into smaller arrays. std::copyis usually specialized for primitive types to give maximum performance. It is unlikely that std::memcpyperforms better. If in doubt, measure.

这比真正的数组分配可以提供的更灵活,因为可以将较大数组的切片复制到较小的数组中。 std::copy通常专门用于原始类型以提供最大性能。不太可能std::memcpy表现更好。如有疑问,请测量。

Although you cannot assign arrays directly, you canassign structs and classes which containarray members. That is because array members are copied memberwiseby the assignment operator which is provided as a default by the compiler. If you define the assignment operator manually for your own struct or class types, you must fall back to manual copying for the array members.

虽然不能直接分配数组,但可以分配包含数组成员的结构和类。这是因为数组成员是由编译器默认提供的赋值运算符按成员复制的。如果您为自己的结构或类类型手动定义赋值运算符,则必须回退到对数组成员进行手动复制。

Parameter passing

参数传递

Arrays cannot be passed by value. You can either pass them by pointer or by reference.

数组不能按值传递。您可以通过指针或引用传递它们。

Pass by pointer

通过指针

Since arrays themselves cannot be passed by value, usually a pointer to their first element is passed by value instead. This is often called "pass by pointer". Since the size of the array is not retrievable via that pointer, you have to pass a second parameter indicating the size of the array (the classic C solution) or a second pointer pointing after the last element of the array (the C++ iterator solution):

由于数组本身不能按值传递,通常指向它们的第一个元素的指针是按值传递的。这通常称为“通过指针”。由于无法通过该指针检索数组的大小,因此您必须传递指示数组大小的第二个参数(经典 C 解决方案)或指向数组最后一个元素之后的第二个指针(C++ 迭代器解决方案) :

#include <numeric>
#include <cstddef>

int sum(const int* p, std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

int sum(const int* p, const int* q)
{
    return std::accumulate(p, q, 0);
}

As a syntactic alternative, you can also declare parameters as T p[], and it means the exact same thing as T* pin the context of parameter lists only:

作为一种语法替代,您还可以将参数声明为T p[],这与T* p仅在参数列表的上下文中的含义完全相同:

int sum(const int p[], std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

You can think of the compiler as rewriting T p[]to T *pin the context of parameter lists only. This special rule is partly responsible for the whole confusion about arrays and pointers. In every other context, declaring something as an array or as a pointer makes a hugedifference.

你能想到的编译器为改写T p[]T *p只有参数列表的背景下。这条特殊规则是造成关于数组和指针的整个混乱的部分原因。在所有其他上下文中,将某些内容声明为数组或指针会产生巨大的差异。

Unfortunately, you can also provide a size in an array parameter which is silently ignored by the compiler. That is, the following three signatures are exactly equivalent, as indicated by the compiler errors:

不幸的是,您还可以在数组参数中提供一个大小,编译器会默默忽略该参数。也就是说,以下三个签名完全等效,如编译器错误所示:

int sum(const int* p, std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[], std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[8], std::size_t n)   // the 8 has no meaning here

Pass by reference

通过引用传递

Arrays can also be passed by reference:

数组也可以通过引用传递:

int sum(const int (&a)[8])
{
    return std::accumulate(a + 0, a + 8, 0);
}

In this case, the array size is significant. Since writing a function that only accepts arrays of exactly 8 elements is of little use, programmers usually write such functions as templates:

在这种情况下,数组大小很重要。由于编写一个只接受恰好 8 个元素的数组的函数没什么用,程序员通常编写这样的函数作为模板:

template <std::size_t n>
int sum(const int (&a)[n])
{
    return std::accumulate(a + 0, a + n, 0);
}

Note that you can only call such a function template with an actual array of integers, not with a pointer to an integer. The size of the array is automatically inferred, and for every size n, a different function is instantiated from the template. You can also write quite usefulfunction templates that abstract from both the element type and from the size.

请注意,您只能使用实际的整数数组调用此类函数模板,而不能使用指向整数的指针。数组的大小是自动推断的,对于每个 size n,从模板实例化不同的函数。您还可以编写非常有用的函数模板,从元素类型和大小中抽象出来。

回答by Cheers and hth. - Alf

5. Common pitfalls when using arrays.

5. 使用数组时的常见陷阱。

5.1 Pitfall: Trusting type-unsafe linking.

5.1 陷阱:信任类型不安全的链接。

OK, you’ve been told, or have found out yourself, that globals (namespace scope variables that can be accessed outside the translation unit) are Evil™. But did you know how truly Evil™ they are? Consider the program below, consisting of two files [main.cpp] and [numbers.cpp]:

好的,您已经被告知或自己发现,全局变量(可以在翻译单元外部访问的命名空间范围变量)是 Evil™。但是您知道它们是多么真实的 Evil™ 吗?考虑下面的程序,它由两个文件 [main.cpp] 和 [numbers.cpp] 组成:

// [main.cpp]
#include <iostream>

extern int* numbers;

int main()
{
    using namespace std;
    for( int i = 0;  i < 42;  ++i )
    {
        cout << (i > 0? ", " : "") << numbers[i];
    }
    cout << endl;
}

// [numbers.cpp]
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

In Windows 7 this compiles and links fine with both MinGW g++ 4.4.1 and Visual C++ 10.0.

在 Windows 7 中,这可以与 MinGW g++ 4.4.1 和 Visual C++ 10.0 编译和链接。

Since the types don't match, the program crashes when you run it.

由于类型不匹配,程序会在您运行时崩溃。

The Windows 7 crash dialog

Windows 7 崩溃对话框

In-the-formal explanation: the program has Undefined Behavior (UB), and instead of crashing it can therefore just hang, or perhaps do nothing, or it can send threating e-mails to the presidents of the USA, Russia, India, China and Switzerland, and make Nasal Daemons fly out of your nose.

正式解释:该程序具有未定义行为 (UB),因此它不会崩溃,因此可以挂起,或者什么也不做,或者它可以向美国、俄罗斯、印度的总统发送威胁电子邮件,china和瑞士,让鼻恶魔从你的鼻子里飞出来。

In-practice explanation: in main.cppthe array is treated as a pointer, placed at the same address as the array. For 32-bit executable this means that the first intvalue in the array, is treated as a pointer. I.e., in main.cppthe numbersvariable contains, or appears to contain, (int*)1. This causes the program to access memory down at very bottom of the address space, which is conventionally reserved and trap-causing. Result: you get a crash.

实践解释:在main.cpp数组中被当作一个指针,放在与数组相同的地址。对于 32 位可执行文件,这意味着int数组中的第一个 值被视为指针。即,在main.cppnumbers变量包含或看似包含(int*)1。这会导致程序在地址空间的最底部访问内存,这通常是保留的并且会导致陷阱。结果:您崩溃了。

The compilers are fully within their rights to not diagnose this error, because C++11 §3.5/10 says, about the requirement of compatible types for the declarations,

编译器完全有权不诊断此错误,因为 C++11 §3.5/10 说,关于声明兼容类型的要求,

[N3290 §3.5/10]
A violation of this rule on type identity does not require a diagnostic.

[N3290 §3.5/10]
在类型标识上违反此规则不需要诊断。

The same paragraph details the variation that is allowed:

同一段落详细说明了允许的变化:

… declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4).

… 数组对象的声明可以指定不同的数组类型,这些类型因是否存在主数组绑定(8.3.4)而异。

This allowed variation does not include declaring a name as an array in one translation unit, and as a pointer in another translation unit.

这种允许的变化不包括将名称声明为一个翻译单元中的数组,以及另一个翻译单元中的指针。

5.2 Pitfall: Doing premature optimization (memset& friends).

5.2 陷阱:过早优化(memset&朋友)。

Not written yet

还没写

5.3 Pitfall: Using the C idiom to get number of elements.

5.3 陷阱:使用 C 习语来获取元素数量。

With deep C experience it’s natural to write …

凭借深厚的 C 经验,编写……

#define N_ITEMS( array )   (sizeof( array )/sizeof( array[0] ))

Since an arraydecays to pointer to first element where needed, the expression sizeof(a)/sizeof(a[0])can also be written as sizeof(a)/sizeof(*a). It means the same, and no matter how it’s written it is the C idiomfor finding the number elements of array.

由于 aarray在需要时衰减为指向第一个元素的指针,因此表达式sizeof(a)/sizeof(a[0])也可以写为 sizeof(a)/sizeof(*a). 它的意思是一样的,不管它怎么写,它都是用于查找数组元素的C 习语

Main pitfall: the C idiom is not typesafe. For example, the code …

主要缺陷:C 习语不是类型安全的。例如,代码...

#include <stdio.h>

#define N_ITEMS( array ) (sizeof( array )/sizeof( *array ))

void display( int const a[7] )
{
    int const   n = N_ITEMS( a );          // Oops.
    printf( "%d elements.\n", n );
}

int main()
{
    int const   moohaha[]   = {1, 2, 3, 4, 5, 6, 7};

    printf( "%d elements, calling display...\n", N_ITEMS( moohaha ) );
    display( moohaha );
}

passes a pointer to N_ITEMS, and therefore most likely produces a wrong result. Compiled as a 32-bit executable in Windows 7 it produces …

将指针传递给N_ITEMS,因此很可能会产生错误的结果。在 Windows 7 中编译为 32 位可执行文件,它会生成……

7 elements, calling display...
1 elements.

7个元素,调用显示...
1个元素。

  1. The compiler rewrites int const a[7]to just int const a[].
  2. The compiler rewrites int const a[]to int const* a.
  3. N_ITEMSis therefore invoked with a pointer.
  4. For a 32-bit executable sizeof(array)(size of a pointer) is then 4.
  5. sizeof(*array)is equivalent to sizeof(int), which for a 32-bit executable is also 4.
  1. 编译器重写int const a[7]为仅int const a[].
  2. 编译器重写int const a[]int const* a.
  3. N_ITEMS因此用指针调用。
  4. 对于 32 位可执行文件sizeof(array)(指针大小),则为 4。
  5. sizeof(*array)等价于sizeof(int),对于 32 位可执行文件也是 4。

In order to detect this error at run time you can do …

为了在运行时检测此错误,您可以执行以下操作……

#include <assert.h>
#include <typeinfo>

#define N_ITEMS( array )       (                               \
    assert((                                                    \
        "N_ITEMS requires an actual array as argument",        \
        typeid( array ) != typeid( &*array )                    \
        )),                                                     \
    sizeof( array )/sizeof( *array )                            \
    )

7 elements, calling display...
Assertion failed: ( "N_ITEMS requires an actual array as argument", typeid( a ) != typeid( &*a ) ), file runtime_detect ion.cpp, line 16

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

7 个元素,调用显示...
断言失败:(“N_ITEMS 需要一个实际数组作为参数”,typeid(a) != typeid(&*a)),文件 runtime_detect ion.cpp,第 16 行

此应用程序已请求运行时以一种不寻常的方式终止它。
请联系应用程序的支持团队了解更多信息。

The runtime error detection is better than no detection, but it wastes a little processor time, and perhaps much more programmer time. Better with detection at compile time! And if you're happy to not support arrays of local types with C++98, then you can do that:

运行时错误检测比不检测要好,但它会浪费一点处理器时间,而且可能会浪费更多的程序员时间。在编译时检测更好!如果您很高兴不支持 C++98 的本地类型数组,那么您可以这样做:

#include <stddef.h>

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

#define N_ITEMS( array )       n_items( array )

Compiling this definition substituted into the first complete program, with g++, I got …

将此定义代入第一个完整程序,使用 g++ 编译,我得到了……

M:\count> g++ compile_time_detection.cpp
compile_time_detection.cpp: In function 'void display(const int*)':
compile_time_detection.cpp:14: error: no matching function for call to 'n_items(const int*&)'

M:\count> _

M:\count> g++ compile_time_detection.cpp
compile_time_detection.cpp: 在函数 'void display(const int*)':
compile_time_detection.cpp:14: 错误: 没有匹配的函数调用 'n_items(const int*&)'

M:\计数> _

How it works: the array is passed by referenceto n_items, and so it does not decay to pointer to first element, and the function can just return the number of elements specified by the type.

工作原理:数组通过引用传递给n_items,因此它不会衰减到指向第一个元素的指针,并且函数可以只返回类型指定的元素数。

With C++11 you can use this also for arrays of local type, and it's the type safe C++ idiomfor finding the number of elements of an array.

使用 C++11,您也可以将它用于本地类型的数组,它是用于查找数组元素数量的类型安全的 C++ 习惯用法

5.4 C++11 & C++14 pitfall: Using a constexprarray size function.

5.4 C++11 & C++14 陷阱:使用constexpr数组大小函数。

With C++11 and later it's natural, but as you'll see dangerous!, to replace the C++03 function

使用 C++11 和更高版本是很自然的,但是你会看到危险!,替换 C++03 函数

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

with

using Size = ptrdiff_t;

template< class Type, Size n >
constexpr auto n_items( Type (&)[n] ) -> Size { return n; }

where the significant change is the use of constexpr, which allows this function to produce a compile time constant.

其中显着的变化是 的使用constexpr,它允许这个函数产生一个编译时常量

For example, in contrast to the C++03 function, such a compile time constant can be used to declare an array of the same size as another:

例如,与 C++03 函数相反,这样的编译时常量可用于声明与另一个相同大小的数组:

// Example 1
void foo()
{
    int const x[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
    constexpr Size n = n_items( x );
    int y[n] = {};
    // Using y here.
}

But consider this code using the constexprversion:

但请考虑使用以下constexpr版本的代码:

// Example 2
template< class Collection >
void foo( Collection const& c )
{
    constexpr int n = n_items( c );     // Not in C++14!
    // Use c here
}

auto main() -> int
{
    int x[42];
    foo( x );
}

The pitfall: as of July 2015 the above compiles with MinGW-64 5.1.0 with -pedantic-errors, and, testing with the online compilers at gcc.godbolt.org/, also with clang 3.0 and clang 3.2, but not with clang 3.3, 3.4.1, 3.5.0, 3.5.1, 3.6 (rc1) or 3.7 (experimental). And important for the Windows platform, it does not compile with Visual C++ 2015. The reason is a C++11/C++14 statement about use of references in constexprexpressions:

陷阱:截至 2015 年 7 月,以上使用 MinGW-64 5.1.0 编译 -pedantic-errors,并使用gcc.godbolt.org/上的在线编译器进行测试,也使用 clang 3.0 和 clang 3.2,但不使用 clang 3.3、3.4。 1、3.5.0、3.5.1、3.6 (rc1) 或 3.7(实验性)。并且对于 Windows 平台很重要,它不能与 Visual C++ 2015 一起编译。原因是关于在constexpr表达式中使用引用的 C++11/C++14 声明:

C++11 C++14 $5.19/2 九th破折号

A conditional-expressioneis a core constant expressionunless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
        ?

  • an id-expressionthat refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is initialized with a constant expression or
    • it is a non-static data member of an object whose lifetime began within the evaluation of e;

条件表达式e是一个核心常量表达式除非的评价e,如下所述抽象机(1.9),将评估下面的表达式中的一个的规则:
        ?

  • 引用变量或引用类型数据成员的id 表达式,除非该引用具有前面的初始化并且
    • 它用常量表达式初始化或
    • 它是一个对象的非静态数据成员,其生命周期开始于 e 的计算;

One can always write the more verbose

一个人总是可以写得越冗长

// Example 3  --  limited

using Size = ptrdiff_t;

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = std::extent< decltype( c ) >::value;
    // Use c here
}

… but this fails when Collectionis not a raw array.

...但是当Collection它不是原始数组时会失败。

To deal with collections that can be non-arrays one needs the overloadability of an n_itemsfunction, but also, for compile time use one needs a compile time representation of the array size. And the classic C++03 solution, which works fine also in C++11 and C++14, is to let the function report its result not as a value but via its function result type. For example like this:

为了处理可以是非数组的集合,需要n_items函数的可重载 性,而且,对于编译时使用,需要数组大小的编译时表示。经典的 C++03 解决方案在 C++11 和 C++14 中也能正常工作,它是让函数报告其结果而不是作为值,而是通过其函数结果类型。例如像这样:

// Example 4 - OK (not ideal, but portable and safe)

#include <array>
#include <stddef.h>

using Size = ptrdiff_t;

template< Size n >
struct Size_carrier
{
    char sizer[n];
};

template< class Type, Size n >
auto static_n_items( Type (&)[n] )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

template< class Type, size_t n >        // size_t for g++
auto static_n_items( std::array<Type, n> const& )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

#define STATIC_N_ITEMS( c ) \
    static_cast<Size>( sizeof( static_n_items( c ).sizer ) )

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = STATIC_N_ITEMS( c );
    // Use c here
    (void) c;
}

auto main() -> int
{
    int x[42];
    std::array<int, 43> y;
    foo( x );
    foo( y );
}

About the choice of return type for static_n_items: this code doesn't use std::integral_constantbecause with std::integral_constantthe result is represented directly as a constexprvalue, reintroducing the original problem. Instead of a Size_carrierclass one can let the function directly return a reference to an array. However, not everybody is familiar with that syntax.

关于 for 返回类型的选择static_n_items:这段代码没有使用,std::integral_constant因为std::integral_constant结果直接表示为一个constexpr值,重新引入原始问题。Size_carrier可以让函数直接返回对数组的引用,而不是类。然而,并不是每个人都熟悉这种语法。

About the naming: part of this solution to the constexpr-invalid-due-to-reference problem is to make the choice of compile time constant explicit.

关于命名:此constexpr-invalid-due-to-reference 问题的解决方案的一部分是明确编译时常量的选择。

Hopefully the oops-there-was-a-reference-involved-in-your-constexprissue will be fixed with C++17, but until then a macro like the STATIC_N_ITEMSabove yields portability, e.g. to the clang and Visual C++ compilers, retaining type safety.

希望 oops-there-was-a-reference-involved-in-your-constexpr问题将用 C++17 修复,但在那之前,像STATIC_N_ITEMS上面这样的宏会产生可移植性,例如,对于 clang 和 Visual C++ 编译器,保留类型安全。

Related: macros do not respect scopes, so to avoid name collisions it can be a good idea to use a name prefix, e.g. MYLIB_STATIC_N_ITEMS.

相关:宏不考虑作用域,因此为了避免名称冲突,最好使用名称前缀,例如MYLIB_STATIC_N_ITEMS.

回答by fredoverflow

Array creation and initialization

数组创建和初始化

As with any other kind of C++ object, arrays can be stored either directly in named variables (then the size must be a compile-time constant; C++ does not support VLAs), or they can be stored anonymously on the heap and accessed indirectly via pointers (only then can the size be computed at runtime).

与任何其他类型的 C++ 对象一样,数组可以直接存储在命名变量中(那么大小必须是编译时常量;C++ 不支持 VLA),或者它们可以匿名存储在堆上并通过以下方式间接访问指针(只有这样才能在运行时计算大小)。

Automatic arrays

自动阵列

Automatic arrays (arrays living "on the stack") are created each time the flow of control passes through the definition of a non-static local array variable:

每次控制流通过非静态局部数组变量的定义时,都会创建自动数组(位于“堆栈上”的数组):

void foo()
{
    int automatic_array[8];
}

Initialization is performed in ascending order. Note that the initial values depend on the element type T:

初始化按升序执行。请注意,初始值取决于元素类型T

  • If Tis a POD(like intin the above example), no initialization takes place.
  • Otherwise, the default-constructor of Tinitializes all the elements.
  • If Tprovides no accessible default-constructor, the program does not compile.
  • 如果TPODint如上例所示),则不会进行初始化。
  • 否则,默认构造函数T初始化所有元素。
  • 如果T没有提供可访问的默认构造函数,则程序不会编译。

Alternatively, the initial values can be explicitly specified in the array initializer, a comma-separated list surrounded by curly brackets:

或者,可以在数组 initializer 中明确指定初始值,这是一个用大括号括起来的逗号分隔列表:

    int primes[8] = {2, 3, 5, 7, 11, 13, 17, 19};

Since in this case the number of elements in the array initializer is equal to the size of the array, specifying the size manually is redundant. It can automatically be deduced by the compiler:

由于在这种情况下数组初始值设定项中的元素数等于数组的大小,因此手动指定大小是多余的。它可以由编译器自动推导出:

    int primes[] = {2, 3, 5, 7, 11, 13, 17, 19};   // size 8 is deduced

It is also possible to specify the size and provide a shorter array initializer:

还可以指定大小并提供更短的数组初始值设定项:

    int fibonacci[50] = {0, 1, 1};   // 47 trailing zeros are deduced

In that case, the remaining elements are zero-initialized. Note that C++ allows an empty array initializer (all elements are zero-initialized), whereas C89 does not (at least one value is required). Also note that array initializers can only be used to initializearrays; they cannot later be used in assignments.

在这种情况下,其余元素是零初始化的。请注意,C++ 允许空数组初始值设定项(所有元素都初始化为零),而 C89 不允许(至少需要一个值)。还要注意,数组初始值设定项只能用于初始化数组;以后不能在作业中使用它们。

Static arrays

静态数组

Static arrays (arrays living "in the data segment") are local array variables defined with the statickeyword and array variables at namespace scope ("global variables"):

静态数组(位于“数据段”中的数组)是使用static关键字和命名空间范围内的数组变量(“全局变量”)定义的局部数组变量:

int global_static_array[8];

void foo()
{
    static int local_static_array[8];
}

(Note that variables at namespace scope are implicitly static. Adding the statickeyword to their definition has a completely different, deprecated meaning.)

(请注意,命名空间范围内的变量是隐式静态的。将static关键字添加到它们的定义中具有完全不同的、已弃用的含义。)

Here is how static arrays behave differently from automatic arrays:

以下是静态数组与自动数组的不同之处:

  • Static arrays without an array initializer are zero-initialized prior to any further potential initialization.
  • Static POD arrays are initialized exactly once, and the initial values are typicallybaked into the executable, in which case there is no initialization cost at runtime. This is not always the most space-efficient solution, however, and it is not required by the standard.
  • Static non-POD arrays are initialized the first timethe flow of control passes through their definition. In the case of local static arrays, that may never happen if the function is never called.
  • 没有数组初始值设定项的静态数组在任何进一步的潜在初始化之前被零初始化。
  • 静态 POD 数组只初始化一次,并且初始值通常被烘焙到可执行文件中,在这种情况下,在运行时没有初始化成本。然而,这并不总是最节省空间的解决方案,并且不是标准所要求的。
  • 静态非 POD 数组在控制流第一次通过它们的定义被初始化。在本地静态数组的情况下,如果从未调用该函数,则可能永远不会发生这种情况。

(None of the above is specific to arrays. These rules apply equally well to other kinds of static objects.)

(以上都不是特定于数组的。这些规则同样适用于其他类型的静态对象。)

Array data members

数组数据成员

Array data members are created when their owning object is created. Unfortunately, C++03 provides no means to initialize arrays in the member initializer list, so initialization must be faked with assignments:

数组数据成员在创建它们的对象时创建。不幸的是,C++03 没有提供在成员初始化列表中初始化数组的方法,因此必须用赋值来伪造初始化:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        primes[0] = 2;
        primes[1] = 3;
        primes[2] = 5;
        // ...
    }
};

Alternatively, you can define an automatic array in the constructor body and copy the elements over:

或者,您可以在构造函数主体中定义一个自动数组并复制元素:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        int local_array[] = {2, 3, 5, 7, 11, 13, 17, 19};
        std::copy(local_array + 0, local_array + 8, primes + 0);
    }
};

In C++0x, arrays canbe initialized in the member initializer list thanks to uniform initialization:

在 C++0x 中,由于统一初始化可以在成员初始化列表中初始化数组:

class Foo
{
    int primes[8];

public:

    Foo() : primes { 2, 3, 5, 7, 11, 13, 17, 19 }
    {
    }
};

This is the only solution that works with element types that have no default constructor.

这是唯一适用于没有默认构造函数的元素类型的解决方案。

Dynamic arrays

动态数组

Dynamic arrays have no names, hence the only means of accessing them is via pointers. Because they have no names, I will refer to them as "anonymous arrays" from now on.

动态数组没有名称,因此访问它们的唯一方法是通过指针。因为它们没有名字,所以从现在开始我将它们称为“匿名数组”。

In C, anonymous arrays are created via mallocand friends. In C++, anonymous arrays are created using the new T[size]syntax which returns a pointer to the first element of an anonymous array:

在 C 中,匿名数组是通过malloc和朋友创建的。在 C++ 中,匿名数组是使用new T[size]返回指向匿名数组第一个元素的指针的语法创建的:

std::size_t size = compute_size_at_runtime();
int* p = new int[size];

The following ASCII art depicts the memory layout if the size is computed as 8 at runtime:

如果大小在运行时计算为 8,以下 ASCII 艺术描述了内存布局:

             +---+---+---+---+---+---+---+---+
(anonymous)  |   |   |   |   |   |   |   |   |
             +---+---+---+---+---+---+---+---+
               ^
               |
               |
             +-|-+
          p: | | |                               int*
             +---+

Obviously, anonymous arrays require more memory than named arrays due to the extra pointer that must be stored separately. (There is also some additional overhead on the free store.)

显然,匿名数组比命名数组需要更多的内存,因为必须单独存储额外的指针。(免费商店还有一些额外的开销。)

Note that there is noarray-to-pointer decay going on here. Although evaluating new int[size]does in fact create an arrayof integers, the result of the expression new int[size]is alreadya pointer to a single integer (the first element), notan array of integers or a pointer to an array of integers of unknown size. That would be impossible, because the static type system requires array sizes to be compile-time constants. (Hence, I did not annotate the anonymous array with static type information in the picture.)

请注意,这里没有发生数组到指针的衰减。尽管评估new int[size]事实上确实创建一个阵列的整数,该表达式的结果new int[size]已经一个指向一个整数(第一元件),整数数组或指针未知大小的整数数组。这是不可能的,因为静态类型系统要求数组大小是编译时常量。(因此,我没有用图片中的静态类型信息注释匿名数组。)

Concerning default values for elements, anonymous arrays behave similar to automatic arrays. Normally, anonymous POD arrays are not initialized, but there is a special syntaxthat triggers value-initialization:

关于元素的默认值,匿名数组的行为类似于自动数组。通常,匿名 POD 数组不会被初始化,但是有一个特殊的语法可以触发值初始化:

int* p = new int[some_computed_size]();

(Note the trailing pair of parenthesis right before the semicolon.) Again, C++0x simplifies the rules and allows specifying initial values for anonymous arrays thanks to uniform initialization:

(注意分号前面的一对尾括号。)同样,C++0x 简化了规则,并允许为匿名数组指定初始值,这要归功于统一初始化:

int* p = new int[8] { 2, 3, 5, 7, 11, 13, 17, 19 };

If you are done using an anonymous array, you have to release it back to the system:

如果您使用完匿名数组,则必须将其释放回系统:

delete[] p;

You must release each anonymous array exactly once and then never touch it again afterwards. Not releasing it at all results in a memory leak (or more generally, depending on the element type, a resource leak), and trying to release it multiple times results in undefined behavior. Using the non-array form delete(or free) instead of delete[]to release the array is also undefined behavior.

您必须只释放每个匿名数组一次,然后再也不碰它。根本不释放它会导致内存泄漏(或者更一般地说,取决于元素类型,资源泄漏),并且多次尝试释放它会导致未定义的行为。使用非数组形式delete(或free)代替delete[]释放数组也是未定义行为