在 cpp 中,“new int”和“new int[1]”有什么区别?

In cpp what is the difference between `new int` and `new int[1]`?

提问人:gowerc 提问时间:7/7/2023 更新时间:7/7/2023 访问量:158

问:

我目前正在学习 C++,并且正在努力理解以下两行代码之间的区别:

int* a = new int;
int* b = new int[1];

据我了解,这两个语句都返回一个 int 指针,并且只在堆上为 1 int 分配空间。那么,它们之间有什么实际区别,还是只是同义词?

C++ 堆内存

评论

3赞 NathanOliver 7/7/2023
一个是单个对象,另一个是大小为 1 的数组。外观相同,但类型不同。
5赞 chrysante 7/7/2023
new int生成指向 的指针,生成指向 int 数组的指针。您必须通过 和 通过 解除分配。否则,您的程序将具有未定义的行为,并且很可能在实践中崩溃intnew int[1]adelete abdelete[] b
4赞 chrysante 7/7/2023
@ScottHunter 在实践中,区别在于,编译器将在数组之前分配空间,其中包含一个元素来存储元素的数量,因为这是一个动态属性。然后,在释放期间,程序将访问该动态数组大小,以便能够正确地释放数组。所有公共分配器都必须知道分配的内存区域的大小才能解除分配。为了让程序员的工作更轻松,简单地将内存区域的大小存储在指针之前的标头中,然后分发指针已成为一种常用方法。delete[] b
2赞 Peter 7/7/2023
它们之间的一个区别是 和 定义明确,而 和 都给出未定义的行为。从本质上讲,表达式必须与表达式相对应。delete adelete[] bdelete [] adelete bdeletenew
2赞 Frodyne 7/7/2023
@chrysante 谢谢你解释为什么它们不同,我认为这确实有助于一些人更好地理解:“你必须遵守规则,因为它们就是规则”。但是,我也认为重要的是要强调,您刚才解释的是某些特定编译器中的实现细节,并且任何编译器都可以自由地以他们认为最好的方式实现数组分配 - 所以不要总是依赖这个细节。:)

答:

3赞 Serge Ballesta 7/7/2023 #1

如果你正在学习C++,你不应该关心这里有什么区别吗?

您只应该记住:

  • 当我想分配一个简单的变量时,我将使用 和 。newdelete
  • 当我想分配一个数组时,我将使用 和new[]delete[]
  • 禁止将两者混合使用

在很多例子中,某些编译器成功处理了不正确的代码。甚至在一些例子中,编译器确实将其处理记录为扩展,或者与遗留代码兼容。

但是,如果你现在正在学习 C++,你应该只尝试编写正确的代码,永远不要怀疑是否可以接受轻微的变化。


顺便说一句,正如您在评论中被告知的那样,直接使用显式分配应该只发生在低级代码中,例如对于自定义容器实现。在普通的用户代码中,只需依赖标准容器...

评论

1赞 NathanOliver 7/7/2023
当我想分配一个数组时,我将使用 std::vector ;)
3赞 chrysante 7/7/2023
从说教的角度来看,我强烈不同意。我认为了解事情为什么会这样有助于理解语言语言规则,否则这些规则可能看起来很武断。在这种情况下,有一个很好的理由禁止混合,反之亦然,在一个元素数组的情况下也是如此。newdelete[]
0赞 Serge Ballesta 7/7/2023
@chrysante:语言规则总是任意的,因为它们是特定语言的规则,可能与不同语言的规则不同。如果你想成为一名语言律师,了解规则的原因确实很重要。如果你只想使用一种编程语言,了解(并尊重)规则就足够了。话虽如此,了解一门语言的哲学很重要的。C++的问题在于,它旨在用于不同的级别,因此提供了不同的语言哲学......
0赞 Serge Ballesta 7/7/2023
...它可以在高层次上使用,几乎就像 Java 一样,也可以在非常低的层次上使用,几乎就像汇编语言一样。如果 C 标准库包含在 C++ 标准库中,或者这里的标准页面引用了 C 语言,这不是偶然的:C++ 确实希望能够像 C 一样在低级别使用。
0赞 chrysante 7/7/2023
也许我们对“任意”这个词有不同的概念,但对我来说,可能没有一个规则是任意的,因为人们在每一个规则和决定中都投入了很多思考。这种思考是基于语言应该归档的内容以及如何有效地实现它。由于 C++ 和 Java 具有不同的目标和理念,因此这种思维产生了不同的语言规则。当然,你可以简单地学习规则并收工,但我不认为从长远来看这是非常有益或实用的。了解人们为什么会
2赞 463035818_is_not_an_ai 7/7/2023 #2

实际上,唯一的区别是您必须使用匹配的删除:

int* a = new int;
int* b = new int[1];

delete a;
delete[] b;

但是,从概念上讲,区别在于创建的对象类型。 创建一个整数,即 . 另一方面,创建一个类型为 的数组。在这两种情况下,您都会从新表达式中得到 a。然而,这只是因为 c 数组有点古怪,它不是指向您刚刚创建的对象的指针,而是指向数组第一个元素的指针。两者的值(数组到第一个元素和指向数组的指针)是相同的,但它们的类型不同,混淆两者可能会导致很多混淆。new intintnew int[1]int[1]int*b


PS 你不应该在 C++ 中使用 raw。不是单个整数。也不适用于数组。有智能指针和/用于数组。newstd::arraystd::vector

3赞 chrysante 7/7/2023 #3

正如其他答案中所指出的,规则就是规则,如果你想编写有效的 C++,即使使用未来的编译器,你也必须遵循它们。

但是,在这种情况下,以及大多数其他情况下,规则乍一看似乎是任意的,这是有充分理由的:

表达式 (where )可以是仅在运行时已知的值,它生成指向 s 数组的指针。要解除分配它,请编写 ,其中 是表达式的结果。请注意,您需要指定要分配的数组大小,但不能指定要解除分配的数组大小。 最常见的分配器,用于实现,但是需要知道分配的内存区域的大小才能解除分配。 因此,许多编译器所做的是分配比存储数组所需的内存更多的内存,在分配的内存区域的开头取一小块,并将数组的大小存储在其中。然后,指针将递增到刚好经过存储大小的区域并返回给您。当您释放数组时,程序将递减指针,读取数组大小,并将内存区域的适当大小传递给底层分配器释放函数。new int[N]Nintdelete[] ppnew int[N]newdelete

请注意,这是一个实现细节。不能依赖于在数组之前存储的大小。某些编译器可能会以不同的方式实现数组分配,但这是一种常用方法。C++ 标准仅指定必须始终与 和 匹配。然后,编译器可以自由地做任何它想要或认为最有效的事情。newdeletenew[N]delete[]

我还应该指出,C++委员会可能已经决定要求你显式地将数组的大小传递给这样:delete[]

delete [N] p;

但他们没有,也许是因为让程序员不必自己存储大小似乎更简单。我个人不同意这个决定,但事情就是这样。

这就是为什么在大多数编译器上,并且会分配不同大小的内存区域,并且结果甚至不会指向底层分配器分配的内存区域的开头。因此,当您尝试解除分配时,您的指针与分配器会非常混乱,因为它会被赋予以前从未分发过的解除分配指针。new intnew int[1]new int[1]bdelete bb


还有强制性的说明:除非你有很好的理由选择后者,否则总是更喜欢和。std::vector<T>new T[N]std::make_unique<T>(...)new T(...)

评论

0赞 gowerc 7/7/2023
在接受之前会等待一会儿,看看其他人是否有任何评论或澄清,但到目前为止,对我来说,这是最清晰、解释最清楚的答案,可以对潜在的差异有所了解。