为什么operator new不能构造非常量大小的多维数组?
Why does operator new can't construct multidimensional array of non-constant size?
我可以为一维数组写operator new如下:
int n{3};
new int[n];
它至少分配 sizeof(int) * n
字节。但是当我想创建二维和多维数组时,只有第一维可能是非常量:
int n{3};
new int[n][3]; //ok
new int[n][n]; //error;
为什么会有这样的限制?确定至少要分配 sizeof(int) * n * n
字节有什么困难吗?
本例中的问题不在于确定要分配多少内存。这部分其实很简单,正如你自己注意到的那样。
问题是之后组织对这样一个数组的访问。如果您知道,C++ 中的多维数组是作为具有索引重新映射的线性(一维)数组实现的。例如,当您声明
int a[N][M];
编译器实际上在后台创建了一个 int [N * M]
数组。当您稍后以 a[i][j]
访问它时,后者只是隐含地转换为对 a[i * M + j]
的访问。 C++ 编译器坚持在编译时知道 M
的值(同时,请注意 N
的值根本不参与索引重新计算公式)。
这就是为什么在数组衰减为指针的情况下,多维数组的第一个大小无关紧要,而第二个、第三个和更多大小必须是编译时常量的原因。这也决定了对 new []
施加的限制。
P.S。 C 语言支持可变长度数组,它允许所有大小都是 运行 时间值。这需要额外的努力,比如将 M
和 N
的 运行 时间值与上述示例中的数组 a
一起存储。这最终被认为不适合 C++。
C++ 类型系统不包括运行时绑定的数组。这是一件非常复杂的事情,考虑到它会对模板和重载解析产生影响。已经有提案,但 none 已经进展到被标准化接受的程度。
因此 T[n]
不是有效类型。但是它可以用在 new
-expression 中,因为它有一个特例。 new
-表达式可以是:
new X
,其中 X
是一个类型
new T[n]
,其中 T
是类型,n
不是常量表达式。
请注意,这两种情况都是必需的,因为 T[n]
不是类型,但我们希望在 new
表达式中允许它。
第二点需要多解释一下。它实际上使用 C++ 中缀表示法,因此如果 T
是数组或函数类型,[n]
将位于不同的位置。例如 new int[n][3]
是 OK ,与 typedef int T[3]; new T[n]
相同。但是 new int[3][n]
不是。
如果我们确实允许 new int[3][n]
,return 类型会是什么? int (*)[n]
不是前面提到的 C++ 类型系统的一部分。
我可以为一维数组写operator new如下:
int n{3};
new int[n];
它至少分配 sizeof(int) * n
字节。但是当我想创建二维和多维数组时,只有第一维可能是非常量:
int n{3};
new int[n][3]; //ok
new int[n][n]; //error;
为什么会有这样的限制?确定至少要分配 sizeof(int) * n * n
字节有什么困难吗?
本例中的问题不在于确定要分配多少内存。这部分其实很简单,正如你自己注意到的那样。
问题是之后组织对这样一个数组的访问。如果您知道,C++ 中的多维数组是作为具有索引重新映射的线性(一维)数组实现的。例如,当您声明
int a[N][M];
编译器实际上在后台创建了一个 int [N * M]
数组。当您稍后以 a[i][j]
访问它时,后者只是隐含地转换为对 a[i * M + j]
的访问。 C++ 编译器坚持在编译时知道 M
的值(同时,请注意 N
的值根本不参与索引重新计算公式)。
这就是为什么在数组衰减为指针的情况下,多维数组的第一个大小无关紧要,而第二个、第三个和更多大小必须是编译时常量的原因。这也决定了对 new []
施加的限制。
P.S。 C 语言支持可变长度数组,它允许所有大小都是 运行 时间值。这需要额外的努力,比如将 M
和 N
的 运行 时间值与上述示例中的数组 a
一起存储。这最终被认为不适合 C++。
C++ 类型系统不包括运行时绑定的数组。这是一件非常复杂的事情,考虑到它会对模板和重载解析产生影响。已经有提案,但 none 已经进展到被标准化接受的程度。
因此 T[n]
不是有效类型。但是它可以用在 new
-expression 中,因为它有一个特例。 new
-表达式可以是:
new X
,其中X
是一个类型new T[n]
,其中T
是类型,n
不是常量表达式。
请注意,这两种情况都是必需的,因为 T[n]
不是类型,但我们希望在 new
表达式中允许它。
第二点需要多解释一下。它实际上使用 C++ 中缀表示法,因此如果 T
是数组或函数类型,[n]
将位于不同的位置。例如 new int[n][3]
是 OK ,与 typedef int T[3]; new T[n]
相同。但是 new int[3][n]
不是。
如果我们确实允许 new int[3][n]
,return 类型会是什么? int (*)[n]
不是前面提到的 C++ 类型系统的一部分。