【问题标题】:How to declare a 3-Dimensional Array with very big size in C? [duplicate]如何在 C 中声明一个非常大的 3 维数组? [复制]
【发布时间】:2018-03-12 12:05:04
【问题描述】:

我正在尝试创建一个 3 维数组 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);
  ....
  ....

我的程序可能因内存使用或类似原因停止工作,我不确定...有什么想法吗?

谢谢。

【问题讨论】:

  • m=1500; int S[m][m][m]; 对于堆栈来说太大了。使用malloc
  • 还有i=1 ; i&lt;=m --> i=0 ; i&lt;m
  • int (*S)[m][m] = malloc(m * sizeof *S);
  • 您的索引也超出了数组的范围。 C 中的数组索引是从零开始的,因此在您的情况下为 0 到 m-1。你需要在做任何其他事情之前解决这个问题。
  • memset(S, 0, sizeof(S[1][1][1])*m * m * m); -> memset(S, 0, sizeof S);

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


【解决方案1】:

您必须动态分配如此大的数组。这是一种方法:

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 );

编辑

考虑一下,存储 15003 个 4 字节整数大约需要 12.5 Gb;您将无法在 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] 指向int * 的1500 个元素序列;每个S[i][j] 指向int 的1500 个元素序列。

优势 - 没有一块内存那么大(5 到 10 Kb,取决于指针大小)。

缺点 - 行在内存中不相邻,因此您不能使用单个指针“遍历”整个数组,也不能在单个 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. 我在这里故意使用术语“序列”而不是“数组”,因为SS[i]S[i][j] 都不是数组因此。这些项目中的每一个都是一个指针,而不是一个数组。

【讨论】:

  • 谢谢,但是当你的意思是 if(!S) { // 内存分配失败,在这里救命 } 然后和 S 一起工作?在 S 数组中使用 for i 给出这样的值... S[ 1] [ 1] [ 1]=2,... S[ 1][ 1][ 2]=8... 如果陈述为假它停止(例如,直到 S[1][1][8]=x 并继续到 S [2] [2][1]..... 到 S[m][m][1]... 如何我可以用你说的这个方法吗?
  • 我不知道是否有可能(来自 StackOverFlow 系统)在此处发布代码作为评论,以 100% 了解我的问题...
  • @NakosKotsanis:代码正确并回答了标题中的问题。如果您有其他问题,请阅读How to Ask 并提出新问题。
  • 我确定代码是正确的,但我真的不明白@JohnBobe 所说的如何使用它......除了标题之外,还有一个解释问题的描述。如果我向约翰询问更多细节是因为我没有得到它如何在我的案例中使用它是被禁止的,那么为什么会有“描述字段”并且有可能回复更多细节?....无论如何你知道更好的酋长;)
  • @NakosKotsanis:在“if”语句的主体中,您将记录一个错误并返回(因此,“bail out”);剩下的代码只有在分配成功时才会执行。
【解决方案2】:

您的代码存在多个问题:

VLAs

您的数组是一个可变长度数组。这意味着数组很可能会分配在堆栈上,堆栈的大小是有限的(在 linux 上,默认值为 8192 KiB,如果那是使用的系统)。假设sizeof(int) == 4, 此空间已用m = 128 耗尽。对于m &gt;= 128,您将收到分段错误,因为访问的内存区域超出了堆栈的边界,因此(很可能)未分配的堆内存的地址将被访问。

内存消耗

对于m = 128,该阵列将消耗 8 MiB,考虑到即使是普通的 32 位操作系统也应该能够处理总共 4GiB 的内存,这并没有那么大。但是内存消耗会以 3 的次方增长。对于 m = 1500,总共将使用 ~12.6 GiB(准确地说是 1.35e10 字节)。这远远超出了stack 的大小,如果内存是在堆上分配的,甚至可能会导致问题。上面提到的 32 位系统作为例子会窒息(忽略 PAE 等特殊情况)。

总结

如果您想为m 支持超过 127 的值,则必须在堆上分配内存。这将推动限制以匹配所提供系统可以处理的内存大小。以上所有计算均为粗略估计,未考虑特殊情况,不应按字面意思理解。他们应该对可以处理的上限有一个印象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-11
    • 2010-09-08
    • 2015-05-26
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    相关资源
    最近更新 更多