【问题标题】:Returning multidimensional arrays from a function in C从 C 中的函数返回多维数组
【发布时间】:2018-01-05 22:30:54
【问题描述】:

从 c 中的函数返回多维数组的最佳方法是什么?

假设我们需要在一个函数中生成一个多维数组并在 main 中调用它,最好将它包装在一个结构中还是只返回一个指向堆上内存的指针?

 int *create_array(int rows, int columns){
     int array[rows][columns] = {0};
     return array;
 }

 int main(){

     int row = 10;
     int columns = 2;
     create_array(row,columns); 
 }

上面的代码只是勾勒出我心目中的基本程序。

【问题讨论】:

  • SO上有很多关于这个话题的问题。您看过哪些但未能理解答案?注意Return pointer to local variable,不要这样做。
  • 我希望你的草图最终不会导致实际代码,因为返回一个自动数组的基地址不会带来任何好处,该数组的生命周期在包含函数的那一刻到期返回。
  • 是的,我看到了编译器警告,主要是我看到这个是用结构体或指向指针的指针来处理的,我只想知道规范的方法是什么跨度>
  • 这将帮助您了解heap and the stack之间的区别
  • 结构体也可以在堆上分配

标签: c arrays function pointers multidimensional-array


【解决方案1】:

你不能返回一个数组,但是你可以返回一个普通的指针和文档,被调用者可以把它当作一个指向它已经传递给调用者的维度的多维数组的指针。

(注意返回的指针必须指向动态或静态,而不是自动内存——不要返回指向局部变量的指针!)

它需要一些稍微罗嗦的演员表,可能还有一个宏,但它是可行的:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void* 
multi(int R, int C)
{
    return calloc ( 1, sizeof(int[R][C])  ); //or sizeof(int)*R*C
}


int main()
{
    int (*r_)[3][4] = multi(3,4);
    if(!r_) return EXIT_FAILURE;    

#define r (*r_)
    //emulate C++ a reference  -- r now behaves as an `int r[3][4];`

    //Test that addresses advance as they would in a multi-d array

    int local[3][4];
    assert(&local[1][0]-&local[0][0] == 4); //base example
    assert(&r[1][0]-&r[0][0] == 4);         //"returned" multi-d array

    free(r); //or free(&r) or free(r_) -- here it shouldn't matter


#undef r

    return 0;

}

请注意,指针数组与多维数组不同。 真正的多维数组是一个连续的块,而指针数组(尽管可以使用相同的索引语法)具有更差的引用局部性,因此如果您想要更好的性能,这可能比返回指向指针的指针更可取。

【讨论】:

  • 请注意 VLA 是 C 的最新特性,而 VLA 上的 sizeof 是唯一不是编译时间常数的情况。我不建议在调用calloc 时使用它,而只是calloc(R*C,sizeof(int))
  • 为什么不让multi 返回一个void* 并简单地写:int (*r)[][4] = multi(3,4); if(!r) return EXIT_FAILURE;
【解决方案2】:

这是错误的:

int *create_array(int rows, int columns){
     int array[rows][columns] = {0};
     return array;
}

并且应该产生这样的警告:

prog.c:2:6: note: (near initialization for 'array')
prog.c:3:13: warning: return from incompatible pointer type [-Wincompatible-pointer-types]
      return array;
             ^~~~~
prog.c:3:13: warning: function returns address of local variable [-Wreturn-local-addr]

因为您要返回自动变量的地址;它的生命周期在其相应的函数终止时结束。


您应该在main() 中声明一个双指针,将其传递给函数,为其动态分配内存并返回该指针。或者您可以在main() 中创建数组并将双指针传递给函数。


我想知道如何在堆上分配多维数组并传递它们

为了在堆上分配内存,您可以使用以下两种方法之一,其中涉及 指针

#include <stdio.h>
#include <stdlib.h>

// We return the pointer
int **get(int N, int M) /* Allocate the array */
{
    /* Check if allocation succeeded. (check for NULL pointer) */
    int i, **array;
    array = malloc(N*sizeof(int *));
    for(i = 0 ; i < N ; i++)
        array[i] = malloc( M*sizeof(int) );
    return array;
}

// We don't return the pointer
void getNoReturn(int*** array, int N, int M) {
    /* Check if allocation succeeded. (check for NULL pointer) */
    int i;
    *array = malloc(N*sizeof(int *));
    for(i = 0 ; i < N ; i++)
        (*array)[i] = malloc( M*sizeof(int) );
}

void fill(int** p, int N, int M) {
    int i, j;
    for(i = 0 ; i < N ; i++)
        for(j = 0 ; j < M ; j++)
            p[i][j] = j;
}

void print(int** p, int N, int M) {
    int i, j;
    for(i = 0 ; i < N ; i++)
        for(j = 0 ; j < M ; j++)
            printf("array[%d][%d] = %d\n", i, j, p[i][j]);
}

void freeArray(int** p, int N) {
    int i;
    for(i = 0 ; i < N ; i++)
        free(p[i]);
    free(p);
}

int main(void)
{
    int **p;
    //getNoReturn(&p, 2, 5);
    p = get(2, 5);
    fill(p ,2, 5);
    print(p, 2, 5);
    freeArray(p ,2);
    return 0;
}

选择最适合您风格的。

【讨论】:

  • 是的,这就是为什么我想知道在堆上分配多维数组并传递它们的最佳方法
  • *table[i] 应该是 (*table)[i]
  • void getNoReturn(int*** table, int N, int M): 请不要做三星级程序员,直接返回指针即可。
  • 我在这里没有看到二维数组,而只是一维数组,实际上是 1 个 1D-int-pointer 数组和 N 1D-int-arrays。
  • @alk 你的意思是函数名,对吧?修改了,现在可以了吗? chqrlie 我不是,我只是想提出一种不同的方法......你想让我更新我的答案并提示返回指针吗?
【解决方案3】:

从 c 中的函数返回多维数组的最佳方法是什么?

我的建议是避免这样做,并避免在 C 中使用多维数组(它们不可读且麻烦)。

我建议您将矩阵类型设置为正确的abstract data type,由一些以flexible array member 结尾的struct 表示:

struct mymatrix_st {
  unsigned nbrows, nbcolumns;
  int values[];
};

这里是创建函数(返回一个正确初始化的指向dynamic memory的指针):

struct mymatrix_st*
create_matrix(unsigned mnbrows, unsigned mnbcolumns) {
  if (mnbrows > UINT_MAX/4 || mnbcolumns > UINT_MAX/4
      ||(unsigned long)mnbrows * (unsigned long)mnbcolums
        > UINT_MAX) {
   fprintf(stderr, "too big matrix\n");
   exit(EXIT_FAILURE);
 };
 size_t sz = sizeof(struct mymatrix_st)+(mnbrows*mnbcolumns*sizeof(int));
 struct mymatrix_st*m = malloc(sz);
 if (!m) { 
   perror("malloc mymatrix"); exit(EXIT_FAILURE); };
 m->nbrows = mnbrows;
 m->nbcolumns = mnbcolumns;
 for (unsigned long ix=(unsigned long)mnbrows * (unsigned long)mnbcolumns-1;
      ix>=0; ix--)
   m->values[ix] = 0;
 return m;;
} /*end create_matrix*/

struct mymatrix_st 故意不包含任何内部指针。您可以而且应该使用free 来销毁它。

这里是访问器函数;将其设为static inline 函数并在声明struct mymatrix_stcreate_matrix 的同一标头中定义它,例如

static inline int getmatrix(struct mymatrix_st*m, unsigned row, unsigned col) {
  if (!m) {
     fprintf(stderr, "getmatrix with no matrix\n");
     exit(EXIT_FAILURE);
  };
  if (row >= m->nbrows || col >= m->nbcolumns){
     fprintf(stderr, "getmatrix out of bounds\n");
     exit(EXIT_FAILURE);
  };
  return m->values[row*m->nbcolumns + col];
}

我留给你定义和实现抽象 struct mymatrix_st 类型的其他操作。

(你可以修改代码,也许去掉越界检查,但我不推荐不安全的代码)

【讨论】:

  • IMO,比公认的多 malloc 版本好得多。 (虽然我会删除 m 空检查并且只删除 assert 边界,以便在 NDEBUG 开启时可以优化检查,因为我喜欢快速且稍微危险的代码):D。
【解决方案4】:
int** create_array(int rows, int columns){
    int** array = malloc(rows * sizeof(int*));
    int i;
    for (i=0; i<rows; i++)
        array[i] = malloc(columns * sizeof(int));
    return array;
}

应该可以解决问题。如果你使用int array[rows][columns];,那么一旦函数返回它就死了,你会得到一个UB。您至少应该使用动态内存分配。

【讨论】:

  • 我在这里看不到二维数组,而只是一维数组,实际上是 1 个 1D-int-pointer 数组和 N 1D-int-arrays。
猜你喜欢
  • 2014-07-23
  • 2017-03-06
  • 2012-12-08
  • 2011-07-20
  • 2021-12-04
  • 2021-03-29
  • 2021-09-22
相关资源
最近更新 更多