如何在C中声明一个非常大的三维数组?

时间:2017-10-01 00:31:14

标签: c multidimensional-array initialization out-of-memory

我试图创建一个三维数组 S [] [] [] ......

当尺寸很小时,例如:

  m=40;  
  int S[m][m][m];
  memset(S, 0, sizeof(S[1][1][1])*m * m * m);  //initialize all the array with 0//
  for (i=1 ; i<=m ; i++ )  { 
      k=1;
     for (j=1 ; j<=n ; j++ ) {
       if(statement) {   //if statment true i put in S array a value//
       S[i][i][k]=j;    k++;}

它工作正常(对于像S [40] [40] [40]这样的小尺寸......当尺寸很大时,例如:

  m=1500;
  int S[m][m][m];
  memset(S, 0, sizeof(S[1][1][1])*m * m * m);
  ....
  ....

我的程序停止工作可能是因为内存使用或其他类似的东西,我不确定...不知道吗?

感谢。

2 个答案:

答案 0 :(得分:2)

你必须动态分配这么大的数组。这是一种方法:

m = 1500;
int (*S)[m][m] = calloc( m, sizeof *S );
if ( !S )
{
  fprintf( stderr, “Fatal: unable to allocate array\n” );
  exit( EXIT_FAILURE );
}

for ( i = 0; i < m; i++ )
{
  k = 0;
  for ( j = 0; j < m; j++ )
  {
    if ( expr )
    {
      S[i][i][k++] = j;
    }
  }
}

free( S );

修改

考虑到这一点,它需要大约12.5 Gb来存储1500 3 4字节整数;你无法在32位系统上实现这一点(它最多只能支持4 Gb的虚拟地址空间)。您需要一个64位系统,但即便如此,您仍然无法在单个连续的块中分配那么多空间。

另一种方法是进行零碎的分配,如下:

bool success = true; 
size_t i, j;
m = 1500;
int ***S = calloc( m, sizeof *S );
if ( !S )
  // bail out here

// Breadth-first allocation strategy, we allocate all a[i] first,
// make sure they all succeeded, and *then* allocate each a[i][j].
for ( i = 0; success && i < m; i++ )
{
  S[i] = calloc( m, sizeof *S[i] );
  success = (S[i] != NULL );
}

// If allocating any S[i] failed, free all S[0] through S[i-1], *then*
// free S.  Freeing S alone won't free each S[i].  
if ( !success )
{
  while ( i-- )
  {
    free( S[i] );
   }
  free( S );
  // bail out here
}

// for each S[i], allocate S[i][j].  
for ( size_t i = 0; success && i < m; i++ )
{
  for ( size_t j = 0; success && j < m; j++ )
  {
    S[i][j] = calloc( m, sizeof *S[i][j] );
    success = (S[i][j] != NULL );
  }
}

// Same deal - if any S[i][j] allocation failed, free all S[i][0] through
// S[i][j-1], *then* free all S[0] through S[i], *then* free S.  
if ( !success )
{
  do
  {
    while ( j-- )
      free( S[i][j] );
    free( S[i] );
  } while ( i-- );
  free( S );
  // bail out here
}

此时,您已分配足够的内存来存储m x m x m元素; 像任何3D数组S[i][j][k]一样索引。与3D阵列不同,个人 行在内存中不相邻 - 你最终看起来像这样:


   int ***       int **               int *                   int 
  +---+         +---+                +---+                   +---+
S:|   | ------> |   | S[0]   ------> |   | S[0][0] --------> |   | S[0][0][0]
  +---+         +---+                +---+                   +---+
                |   | S[1]           |   | S[0][1]           |   | S[0][0][1]
                +---+                +---+                   +---+
                 ...                  ...                     ...
                +---+                +---+                   +---+
                |   | S[m-1]         |   | S[0][m-1]         |   | S[0][0][m-1]
                +---+                +---+                   +---+

S指向int **的1500个元素序列 1 ;每个S[i]指向一个1500个元素的int *序列;每个S[i][j]指向一个1500个元素的int序列。

优点 - 没有单个内存块(5到10 Kb,具体取决于指针大小)。

缺点 - 行在内存中不相邻,因此您不能仅仅行走&#34;行走&#34;使用单个指针通过整个数组,您无法在单个memcpyfwrite调用中复制或序列化数组。

如果发生故障,您需要小心回滚任何部分分配 - 只需释放S您为每个S[i]S[i][j]分配的空闲内存。

当您完成后,您需要按照您分配的相反顺序解除分配:

// free in reverse order of allocation
for ( i = 0; i < m; i++ )
{
  for ( j = 0; j < m; j++ )
    free( S[i][j] );
  free( S[i] );
}
free( S );

同样,这假设您的系统可以支持超过16 GB的虚拟地址空间(即64位)。如果没有,你就无法在这么大的时期建造结构。

<小时/>

  1. 我故意使用术语&#34;序列&#34;而不是&#34;数组&#34;这里,因为SS[i]S[i][j]都不是数组。这些项中的每一项都是指针,而不是数组。

答案 1 :(得分:0)

您的代码存在多个问题:

VLAs

您的数组是可变长度数组。这意味着该阵列最有可能在堆栈上分配,其大小有限(在Linux上默认为8192 KiB,如果是使用过的系统)。假设sizeof(int) == 4, 此空间已用尽m = 128。对于m >= 128,您将获得分段错误,因为访问的内存区域超出了堆栈的边界,因此将访问(最可能)未分配的堆内存的地址。

内存消耗

对于m = 128,阵列将消耗8 MiB,即使通常的32位操作系统应该能够处理总共4GiB的内存,也不会 。但是内存消耗增长了3的幂。对于m = 1500,将使用总共~12.6 GiB(准确地说是1.35e10字节)。这超出了stack的大小,如果在堆上分配内存,甚至可能会导致问题。上面提到的32位系统作为一个例子将会扼杀这一点(PAE被忽略的特殊情况)。

摘要

如果要为m支持超过127的值,则必须在堆上分配内存。这将推动限制以匹配所提供系统可以处理的内存大小。所有上述计算均为粗略估计,不考虑特殊情况,不应采用字面意义。他们应该做的是给人一种可以处理的上限的印象。