【问题标题】:How does dynamic array memory allocation work?动态数组内存分配是如何工作的?
【发布时间】:2016-02-25 21:58:15
【问题描述】:

编辑:由于我发布了一个没有源代码的 TArray 的不当问题,因此我将其替换为 std::vector。

我在主函数中定义了一个动态数组

std::vector<sSymbols> arrSymbols; 

我还有一个向数组添加新元素的函数;

functionOne(std::vector<TSymbols>& arrSymbolsRef)
{
    sSymbols symbolOne;
    //some symbol struct filling
    arrSymbolsRef.push_back(symbolOne);
    for (int i = 0; i < arrSymbolsRef.size(); i++) 
       printf("arrSymbolsRef[%d] ptr is %d", i, &arrSymbolsRef[i]);
}

更新结果: 在循环中运行这个函数会得到这个结果:

arrSymbolsRef[0] ptr is -1242393600
arrSymbolsRef[0] ptr is -1257218304
arrSymbolsRef[1] ptr is -1257218244
New iteration
arrSymbolsRef[0] ptr is -1451463936
arrSymbolsRef[1] ptr is -1451463876
arrSymbolsRef[2] ptr is -1451463816
New iteration
arrSymbolsRef[0] ptr is -1450359040
arrSymbolsRef[1] ptr is -1450358980
arrSymbolsRef[2] ptr is -1450358920
arrSymbolsRef[3] ptr is -1450358860
New iteration
arrSymbolsRef[0] ptr is -1243432448
arrSymbolsRef[1] ptr is -1243432388
arrSymbolsRef[2] ptr is -1243432328
arrSymbolsRef[3] ptr is -1243432268
arrSymbolsRef[4] ptr is -1243432208
New iteration
arrSymbolsRef[0] ptr is -1243432448
arrSymbolsRef[1] ptr is -1243432388
arrSymbolsRef[2] ptr is -1243432328
arrSymbolsRef[3] ptr is -1243432268
arrSymbolsRef[4] ptr is -1243432208
arrSymbolsRef[5] ptr is -1243432148
New iteration
arrSymbolsRef[0] ptr is -1550211968
arrSymbolsRef[1] ptr is -1550211908
arrSymbolsRef[2] ptr is -1550211848
arrSymbolsRef[3] ptr is -1550211788
arrSymbolsRef[4] ptr is -1550211728
arrSymbolsRef[5] ptr is -1550211668
arrSymbolsRef[6] ptr is -1550211608

为什么有时地址会发生变化?

我需要创建一个指向这些元素的指针数组,并在每次增加 arrSymbols 时添加新元素(指针)。

但是经过几次迭代后,数组中的指针指向了错误的数据。

【问题讨论】:

  • 很难说没有看到TArray的来源,但似乎这个类会自动将它当前保存的数据重新定位到其他位置。
  • 您的TArray 很可能不得不搬迁,因为它试图保持项目连续,并且无法在其旧地址这样做。在这种情况下,将指针存储到数组中是完全不安全的。
  • 这个问题取决于TArray是什么。那是你自己写的课吗?如果是这样,将有助于显示它的源代码。如果是其他人的,那么至少链接到它的文档
  • 您的代码具有未定义的行为,因为%d 表示您承诺传递int,而不是TSymbols*
  • 魔法。这是我能在这里给出的唯一诚实的答案。我们应该如何知道您的TArray 在做什么?哦,等等,我还有一个给你:“通过电”

标签: c++ arrays memory


【解决方案1】:

就像我在对问题的评论中所说的那样,您不能一直依赖班级的存储在同一个地方。当您向其中添加元素时,它很可能必须增长 - 为此,它将分配一个新的更大的块,并将所有当前数据复制到其中。

仅作记录,std::vector 也不提供这种行为。这个简单的程序:

int main()
{
    std::vector<int> a;
    for(int i = 0 ; i < 17 ; ++i) {
        a.push_back(i);
        std::cout << "Iteration " << i << " : "
                  << static_cast<void*>(&a[0]) << '\n';
    }
}

在我的系统上给出以下结果:

Iteration 1 : 0x1aa9050
Iteration 2 : 0x1aa8c20
Iteration 3 : 0x1aa8c20
Iteration 4 : 0x1aa9070
Iteration 5 : 0x1aa9070
Iteration 6 : 0x1aa9070
Iteration 7 : 0x1aa9070
Iteration 8 : 0x1aa90a0
Iteration 9 : 0x1aa90a0
Iteration 10 : 0x1aa90a0
Iteration 11 : 0x1aa90a0
Iteration 12 : 0x1aa90a0
Iteration 13 : 0x1aa90a0
Iteration 14 : 0x1aa90a0
Iteration 15 : 0x1aa90a0
Iteration 16 : 0x1aa90f0

如您所见,vector 的存储起始地址发生了几次变化。现在,由于std::vector 保证其元素的连续存储,其所有元素的地址也会发生变化。

我认为没有任何可动态调整大小的容器可以保证其元素的地址不变,除非您预先声明了可以存储的最大元素数量。在这种情况下,它可以一次只分配一个大块,而不再重新分配。如果您需要这种行为,请考虑使用 std::array 之类的东西,或者在 std::vector 周围创建一个不允许修改其大小的包装器。

【讨论】:

  • std::list 保证对元素的引用(以及扩展的指针)永远不会失效,只要元素没有被删除。如果您只在前面或后面添加和删除,std::deque 给您同样的保证。您还可以使用智能指针向量,在这种情况下,指针是安全的。
  • @BenjaminLindley:好点。我在精神上将自己限制在保证连续存储的容器上。
猜你喜欢
  • 1970-01-01
  • 2013-12-20
  • 1970-01-01
  • 2023-04-06
  • 2020-08-07
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多