动态数组分配

7
我想知道为什么new似乎不能保留数组信息,只返回一个int*:
#include <iostream>
using namespace std;

typedef int (*p_to_array)[80];
typedef int arr[80];




int main() 
{
   arr a;
   p_to_array p = &a;  //fine

   p_to_array p2 = new arr; // incompatible types 

   return 0;
}

1
信息的丢失被称为衰减,并且只能单向进行。 - Bartek Banachewicz
使用向量,因为它们会记住它们的大小。 - Neil Kirk
在最近的C++版本中,您可以使用array容器类来处理固定大小的集合。 - CodesInChaos
3个回答

6
我可能完全错误,这只是一个猜测:(即这只是一个稍微有点见识的猜测)
new arr;

等同于:

new int[80];

根据语言规则,返回int*


来自cppreference.com:(感谢@interjay指出我实际上引用了无关部分)

new表达式返回构造对象的prvalue指针或者如果构造了对象数组,则返回指向数组初始元素的指针。

我的理解是,在分配数组时,将选择使用new[]表单。您的示例仅更改语法;它不会使语言将数组视为非数组(常规对象)。


2
你引用的cppreference是关于分配函数(operator new)的,但它并不是很相关,因为它返回void*。相关的语言特性是new表达式,具体来说:“new表达式返回一个prvalue指向构造对象的指针,或者如果构造了一个对象数组,则返回指向数组初始元素的指针。”。 - interjay
@interjay 当然是对的。我已经更改引语,以引用 new 表达式 - Bartek Banachewicz

4

您可以使用数组语法或typedef名称来使用new分配数组 - 根据[expr.new]/5,两者均被允许且等效:

当分配对象为数组时(即使用语法或新类型ID或类型ID表示数组类型),new-expression产生指向数组的初始元素(如果有)的指针

正如您所看到的,对于数组,仅返回指向初始元素的指针;不是指向数组对象本身的指针。因此,所产生的类型是int *,而不是int (*) [N]

为什么返回指向初始元素的指针很简单:这是您想要使用的指针。
我们想写成

auto p = new int[50];
// Or
int* p = new int[50];

为了能够使用普通的下标操作:
p[49] = 0xDEADBEEF;

如果p是指向数组对象的int(*)[50]类型,那么使用下标会变得毫无意义,因为由于下标语义和指针算术运算的原因,下标将使地址增加49*50*4而不仅仅是49*4。
我们必须要使用两个下标:

p[0][49] = ...;

这样做既可怕又不必要重复。或者我们可以使用转换:

int* p = static_cast<int*>(new int[50]); // Or with auto

但是那也很丑。
为避免这种麻烦,引入了这个规则。

谢谢。来源是什么?我认为这不会是无意义的,我是通过链接发现的。而不是使用强制转换,你可以使用双重下标。这种方式会丢失信息。 - LogicBreaker
@bla 源自哪里?p[49]是毫无意义的。事实上,它很可能是未定义行为。 - Columbo
@bla 他说在 [expr.new]/5。这是指 C++ 编程语言标准的一个章节。 - Bartek Banachewicz
@bla 不是很清楚,你具体有什么问题? - Columbo
在所有权语义的上下文中,你失去了指针算术,更不用说边界检查了。 - LogicBreaker
显示剩余2条评论

1
在这个声明中
p_to_array p2 = new arr; // incompatible types 

初始化器的类型 new arr; 与变量的类型 p2 不匹配。

初始化器的类型是 int *,即指向分配数组的初始元素的指针。根据 typedef 定义,变量 p2 的类型是 int (*p_to_array)[80]

从类型 int * 到类型 int (*p_to_array)[80] 没有隐式转换,因此编译器会报错。

正确的声明可能如下所示:

p_to_array p2 = new arr[1];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接