【问题标题】:Preventing array decay when allocating memory on the heap?在堆上分配内存时防止数组衰减?
【发布时间】:2018-01-29 04:36:53
【问题描述】:

如果我写int* a = new int[5];,然后我调用sizeof(a)sizeof(*a),我不会得到我想要的信息,因为数组已经衰减为一个指针。但是,我想知道是否有办法可以做这样的事情:

int[5]* a = new int[5];

我很确定我们可以使用堆栈内存来做到这一点,但我不确定是否有任何方法可以为堆内存做到这一点,因为上述内容无法编译。 如果没有办法,是否有理由不考虑这种可能性?

【问题讨论】:

  • 你可以通过 std::vector<int> a(5)a.size() 来阻止它...
  • 没有数组衰减
  • @TheDude 简单的误解。您应该只编辑问题。

标签: c++ arrays pointers


【解决方案1】:

C++ 最初只是 C 的前端,因此无法回避原始语言的大量包袱。比如<cXXX>标头中的很多东西,以及很多操作的行为,比如数组衰减(a)

如果你想要更方便的数组类型,C++std::vector 中有这样的野兽。如果您传递它,您将不会受到使用更底层内存分配方法时可能看到的衰减的影响。参见,例如:

#include <iostream>
#include <vector>

void someFunction (const std::vector<int> &myVec) {
    std::cout << myVec.size() << std::endl;
}

int main() {
    std::vector<int> myVec({ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9, 0 });
    std::cout << myVec.size() << ' ';
    someFunction(myVec);
}

输出13 13,表明函数内部和外部的大小相同(它也是elements中的大小,而不是给定bt sizeof的字节,前者通常是更有用的措施)。


在指导或培训 C++ 程序员时,我总是警告他们要成为一名“C+”程序员,他们是在 C 环境中长大的,但从未真正完全过渡到 C++(比如继续使用原始数组而不是向量,滚动当标准库中有完全足够的数据结构时,您就拥有了数据结构,使用malloc/freeprintf,等等)。

您会明智地认为 C 和 C++ 是相关但完全不同的语言,并尽可能采用 C++ 的思维方式。


(a) 有趣的是,您的具体问题 not 是否有一个数组衰减为指针。 new 调用实际上直接返回一个指针,因此不涉及衰减。

看到衰变的地方是:

void someFunction (int x[]) {
    // Pointer to first element here, size is int-pointer-size.

    std::cout << sizeof(x) << std::endl;
}

:

// Array here, size is 13 * int-size.

int xyzzy[] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9, 0 };
std::cout << sizeof(xyzzy) << std::endl;

someFunction(xyzzy);

【讨论】:

    【解决方案2】:

    没有办法将此信息存储在指针中。并且没有单独的构造可以让您以您想要的方式执行此操作。您的选择是:

    1. 将数组的大小与数组分开存储:const int a_size = 5; int *a = new int[a_size]
    2. 使用 C++ 构造为您处理此问题:std::vector&lt;int&gt; a(5); a.size()

    我强烈推荐选项 2。不仅仅是因为它更简单,而且std::vector 提供了更多功能。但是因为 RAII 样式结构会为您管理内存,这意味着您以后不需要记住在内存上调用 delete[]。您也不能稍后在内存上意外调用delete,因为您可能忘记了它是一个数组。

    【讨论】:

    • 谢谢!有什么理由可以声明一个指向 int[5] 类型的堆栈上的数组的指针,例如,而不是堆上的?
    • @rb612 是的,你可以有一个指向你想要的任何东西的指针。例如。 int a[5]; int *pa = &amp;a[0]。但是如果你使用自动存储时长记忆,你应该使用std::array&lt;int, 5&gt;而不是int a[5]
    【解决方案3】:

    当然你可以,通过二维数组,例如

    int (*a)[5] = new int[1][5];
    

    注意:此代码仅用于语言律师的目的,不建议在实践中使用。 如果这是一个 XY 问题,您应该使用 std::vectorstd::array 作为其他答案的建议

    new int[N] 返回指向其第一个元素的指针而不是像非数组 new 那样指向自身的指针的原因之一是为了使数组的使用更容易。例如,使用auto a = new int[5],您可以通过简单的a[n] 而不是(*a)[n] 访问数组。

    【讨论】:

    • 这不像将信息存储在指针中,您正在创建一个完全不必要的数组!此外,您要分配给指针,而不是数组。这是非常不同的。见stackoverflow.com/a/3960723/1294207
    • @FantasticMrFox 但是 OP 既不要求在指针中存储信息,也不禁止 a 成为指针。请注意他说“然后我打电话给sizeof(a)sizeof(*a),我没有得到我想要的信息”。现在他可以拨打sizeof(*a)
    • 投反对票的人能否解释一下为什么投反对票? OP 要求一种分配具有动态存储持续时间的数组的方法,该数组能够访问数组长度信息。我的回答确实是这样的:它确实分配了一个数组(虽然是另一个二维数组的子对象),数组长度信息可以通过sizeof(*a)访问。
    • 我的是因为,虽然从技术上讲这是可能的并且有效。这不是实现这一目标的好方法。如果未来的用户阅读并决定这样做,即使它在技术上是正确的,他们将使用一个危险的构造,当 std::vector 存在时他们不需要使用它并且它使用 5 个指针来完成 1 + 1 int 的工作.我知道你有免责声明,但新人很少会深入阅读任何问题。如果你提供一种你应该这样做的方式,我会取消投票。这可能是答案底部的一个想法。
    • OP 没有正确执行。您正在以提供正确答案的方式进行操作,但教任何阅读此内容的人并不是一件好事。 IMO,您给出的答案可能(并且应该)是 1。这是正确的方法。 2. 这是另一种方法。 3. 这里有一个有趣的点,即指针数组的大小恰好与 5 个整数的数组相同。因为考虑一下OP何时扩展它。如果他们想要 5000 个整数怎么办?您是否要分配 5000 个指针,以便获得数字 5000 作为大小?
    猜你喜欢
    • 2014-09-27
    • 2018-09-17
    • 2013-08-10
    • 2017-11-08
    • 2018-03-24
    • 2011-11-03
    • 1970-01-01
    • 2017-06-25
    • 2021-05-10
    相关资源
    最近更新 更多