【问题标题】:Can i make a dynamic array this way?我可以这样制作动态数组吗?
【发布时间】:2017-06-04 06:34:23
【问题描述】:

所以我的代码是这样的:

int a, b;
printf("Give dimensions\n");
scanf("%d %d", &a, &b);
double array1[a][b];
printf("Give values\n");
int i, j;
for(i=0; i<a; i++)
{
    for(j=0; j<b; j++)
    {
        scanf("%lf", &array1[i][j]);
    }
}

我的问题是,有人告诉我这是一种错误的内存分配方式,我应该使用 malloc。我应该使用用户的维度创建一个动态数组。

编辑:程序的其余部分:

double sum=0;
for(i=0; i<a; i++)
{
    for(j=0; j<b; j++)
    {
        sum = sum + array1[i][j];
    }
    printf("Line %d: sum = %lf\n", i+1, sum);
    sum=0;
}

【问题讨论】:

  • 请注意,在继续分配 VLA 之前,您应该验证读取 (scanf()) 是否成功,并且读取的值是合理的(严格为正且不太大)。您还应该检查其他输入操作 - 如果输入失败,请采取适当的措施。

标签: c arrays dynamic-memory-allocation variable-length-array


【解决方案1】:

取决于您如何定义“正确”。这是自 C99 以来的合法 C。

但问题是,如果ab 太大,数组将溢出调用堆栈。如果这是一种可能的情况,您应该更喜欢malloc。与堆相比,调用堆栈通常分配得相当小。所以这可能是建议的来源。

请注意,您仍然可以使用动态分配的数组来享受数组下标表示法:

double (*array)[b] = malloc(sizeof(double[a][b]));

数组在一个连续的块中,VLA 指针将导致a[i][j] 引用正确的元素。

【讨论】:

    【解决方案2】:

    不,这是完全正确且有效的方式,只要您使用支持Variable-length array 的编译器版本/环境。

    这是C99 上的强制性功能,但在C11 上再次成为可选功能。

    使用 VLA 和“指针和内存分配器功能组合”之间的主要区别是

    • VLA 是块范围的。 VLA 不在作用域之外,因此它不能从函数返回并在调用者中使用,这与指针和 malloc 方法不同。
    • 通常 VLA 在堆栈中分配给所有主要实现,因此大小受到限制。

    【讨论】:

      【解决方案3】:

      C 编译器可以将其作为有效代码处理,尽管 C11 standard made VLA support optional.

      主要问题是实际上您无法检查分配是否成功,尤其是在大小未知的情况下。这也是malloc/calloc的主要优势:

      double (*array1)[b] = malloc(a * sizeof(*array1));
      if (!array1) {
           // handle allocation failure
      }
      

      【讨论】:

        【解决方案4】:
        scanf("%d %d", &a, &b);
        double array1[a][b];
        

        这在 C99 标准中是有效的,尽管您需要先对输入进行一些健全性检查(即,确保两个输入都被实际读取,确保两者都不是负数,确保它们在合理范围等)。

        array1 是一个可变长度数组 (VLA),最初是在 C99 中引入的。自 C2011 起,它们已成为可选,但我认为几乎所有托管实现仍然支持它们。为确保这一点,请检查 __STDC_NO_VLA__ 宏 - 如果已定义,则实现支持 VLA。

        size_t a, b;
        if ( scanf( "%zu %zu", &a, &b ) < 2 )
          // bail out with an error message
        
        #if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L && !defined( __STDC_NO_VLA__ ) 
        
          /**
           * This implementation supports VLAs.  We need to do an additional
           * sanity check on a and b to make sure we don't allocate an
           * object too large for the stack (even though VLAs
           * don't have to be allocated on the stack, that's how many 
           * implementations choose to do it).
           */
          if ( a > SOME_MAX_LIMIT || b > SOME_MAX_LIMIT )
            // bail out with an error
          double array1[a][b];
        
        #else  
        
          /**
           * This implementation does not support VLAs, so we'll have to use
           * dynamic memory allocation.  Note that memory allocated this way
           * is *not* contiguous - rows are not adjacent, so the object immediately
           * following array1[0][b-1] is not array1[1][0].  If that matters, 
           * then this won't work.  
           */
          double **array1 = malloc( sizeof *array1 * a );
          if ( array1 )
          {
            size_t i;
            for ( i = 0; i < a; i++ )
            {
              array1[i] = malloc( sizeof *array1[i] * b );
              if ( !array1[i] )
                break;
            }
            if ( i < a ) // memory allocation failure, clean up any previously allocated memory
            {
              while ( i-- )
                free( array1[i] );
              free( array1 );
              // bail out with an error here
            }
          }
          else
            // memory allocation failed, bail with an error
        
        #endif
        

        此时,您的代码可以引用array1[i][j],无论我们以哪种方式分配它。但是,您需要在函数末尾添加:

        #if !defined( __STDC_VERSION__ ) || __STDC_VERSION__ < 199901L || defined( __STDC_NO_VLA__ )
        
          for ( size_t i = 0; i < a; i++ )
            free( array1[i] );
        
          free( array1 );
        
        #endif
        

        以便我们在必须使用 malloc 时自行清理。

        VLA 的行为与其他所有 auto(本地)数组非常相似 - 当您退出函数时,它们的内存将被释放,因此您不能返回指向它们的指针并让指针在函数之后有效退出。

        由于它们的大小直到运行时才确定,所以不能在文件范围内使用它们(在任何函数之外),不能将它们声明为 static,并且它们不能是 struct 的成员或union 类型。

        您可以将 VLA 与动态内存分配结合起来:

        size_t a, b;
        if ( scanf( "%zu %zu", &a, &b ) < 2 )
          // bail out with an error
        
        double (*array1)[b] = malloc( sizeof *array1 * a );
        

        您将获得一个动态分配的二维数组,该数组具有用户指定的维度, 是连续的,并且您仅受堆大小的限制,而不是堆栈大小,因此您可以通过此分配大对象大大地。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-04
          • 2021-04-12
          • 2011-07-07
          相关资源
          最近更新 更多