【问题标题】:Can malloc be relied on to return contiguous memory, and how do I properly call it?可以依靠 malloc 来返回连续的内存,我该如何正确调用它?
【发布时间】:2014-09-22 14:26:49
【问题描述】:

在开始之前,是的,我已经阅读了a possible duplicatemalloc being weird in linuxcplusplus.com on malloc 并在 google 上进行了一些搜索。

我有一个需要非常大的二维数组的科学计算问题。我正在关注“C 中的数字食谱”副本中的代码,并且在我的二维数组中间遇到了未分配内存的问题。我在 Windows 中工作,并且在 MSVC 2012 中使用 c++。

这是我的二维数组分配

  unsigned long nrl=0;
  unsigned long nrh=749774;
  unsigned long ncl=0
  unsigned long nch=250657;
  unsigned long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
  double **m;
  if((size_t)(nrow*ncol)<(size_t)nrow){
    m=NULL;
    return m;
  }
  /*allocate pointers to rows*/
  m=(double **)malloc((size_t)(nrow)*sizeof(double*));
  if (!m){
    m=NULL;
    return m;
  }

  /*allocate rows and set pointers to them*/
  m[nrl]=(double *) malloc((size_t)((nrow*ncol)*sizeof(double)));
  if(!m[nrl]){  
    free(m[nrl]);
    free(m);
    m=NULL;
    return m;
  }
  for(i=nrl+1;i<=nrh;i++)m[i]=m[i-1]+ncol;
  /*The 2D array should now be callable as m[nrow][ncol]*/
  /*Pseudo-code below*/
  m[0][0] == Good, allocated memory
  m[125][200923] == Unallocated, crashes program
  m[nrh-1][nch-1] == Good, allocated memory

如果内存分配失败,我目前依靠 malloc 返回 NULL(如果我尝试分配非常非常大的数组,我实际上会得到 NULL 值。

另外,我尝试了double *m = new double[nch*nrh],但这给了我一个内存分配错误。我愿意接受有关替代实现的任何建议,但我需要能够知道分配是否有效并在必要时重新分配较小的块。

编辑:

这是一个 c 函数,但我的大部分代码都是用 c++ 编写的。

更新:

感谢大卫,我能够解决这个问题。从

更改我的溢出检查
if((size_t)(nrow*ncol)<(size_t)nrow)

if(SIZE_MAX/nrow < ncol || SIZE_MAX/ncol < nrow || nrow*ncol<nrow)

允许 malloc 在它应该失败时失败。

【问题讨论】:

  • 好吧,您正在尝试分配 1.36 TB 的数据,所以是的,这会给您带来内存分配错误。
  • ((size_t)nrow)*((size_t)ncol)(size_t)(nrow*ncol) 更有意义。
  • 我认为不需要投射。他们最终是int。如果数组很大,你应该注意溢出问题。如果它很大,我认为你也不需要考虑连续分配,因为你希望它只有在所有数据都适合缓存时才是连续的。
  • 很遗憾您使用的是 MSVC,因为它们不符合标准。使用真正的现代 C 编译器,您将拥有真正的 2D 动态矩阵,而您不必使用您正在使用的这些假矩阵。在 C99 中,这只是 double (*m)[ncol] = malloc(sizeof(double[nrow][ncol]));。没有多重分配,没有显式大小计算,......也不要使用 C++ 编译器来编译 C。这是两种不同的语言。
  • @JensGustedt 导致这种情况的不是 MSVC 的选择,而是代码来自 NR 并且完全古老的事实。

标签: c arrays memory-management


【解决方案1】:

我猜你有一个 32 位进程。请注意nrow*ncol*sizeof(double) (很多!)大于 2^32。您实际上是在尝试分配 1400 GB,我想这会让您感到惊讶。

很可能无法分配那么多内存,当然也不能在 32 位进程中分配。您的代码似乎可以运行,因为(nrow*ncol)*sizeof(double) 遭受整数溢出,因此您对malloc 的调用成功,但未分配您期望的内存。事实上,即使在 64 位进程中也会出现溢出,因为 nrow*ncol 是用 32 位算术计算的,因为您将 nrowncol 声明为 unsigned long。他们真的应该是size_t

无论如何,您将需要重新考虑解决问题的整个方法。您真的可以期望使用 1400 GB 密集矩阵做任何有用的事情吗?

【讨论】:

  • 注意:DOS 16 位机器(在大内存模型中)可以分配 > 64k 内存。 IOW,作为“32 位机器”并不将内存限制为 4Gbyte。内存容量,虽然通常与拥有者整数大小有关,但却是独立的因素。
  • 在这种情况下,32 位或 64 位并不重要...当然,您可以在 64 位地址空间中容纳 1.36 TB,但 Windows 8 最多仅支持 512 GB。 Windows Server 2012 最多可以支持 4 TB,但我猜如果这个人买得起那种硬件,问题就会大不相同了。
  • @MadScienceDreams 好吧,这确实很重要,因为故障模式不同。在 64 位上,对 malloc 的调用将失败,因为至少传递的 size_t 是预期值。
  • @MadScienceDreams 嗯,这是先将 nrow 和 ncol 模转换为 size_t
  • 如果 size_t 是 64 位,long 是 32,则在转换为 64 位之前,nrow*ncol 会发生溢出。
猜你喜欢
  • 2011-12-18
  • 1970-01-01
  • 2019-07-07
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多