【问题标题】:Declaring a C function to return an array声明一个 C 函数以返回一个数组
【发布时间】:2010-11-30 00:26:39
【问题描述】:

如何创建一个返回数组的函数?我试过这个

const int WIDTH=11;
const int HEIGHT=11;

int main() {
  char A[WIDTH][HEIGHT];
  A=rand_grid(WIDTH,HEIGHT);
  return 0;
}

// Initializes a random board.
char[][] rand_grid(int i, int k) {
  char* A[i][k];
  for(j=0;j<i;++j) {
    for(l=0;l<k;++l) {
      A[j][l]=ran(10);
    }
  }
  return A;
}

// Returns a random number from the set {0,...,9}.
int ran(int i) {
  srand((unsigned int) time(0));
  return(rand()%10);
}

【问题讨论】:

  • 不要在程序中多次调用 srand():这会不必要地减慢程序速度,最重要的是,它会降低 rand() 函数的随机性。

标签: c


【解决方案1】:

有几点需要指出。

首先,你不能像这里那样分配一个数组对象:

char A[WIDTH][HEIGHT];  
A=rand_grid(WIDTH,HEIGHT);

数组类型的对象是不可修改的。

其次,C 中的函数不能返回数组类型。不过,它们可以将 指针 返回到数组:

char (*foo(int width))[HEIGHT]
{
  /**
   * dynamically allocate memory for a widthxHEIGHT array of char
   */
  char (*newArr)[HEIGHT] = malloc(sizeof *newArr * width);
  /**
   * initialize array contents here
   */
  return newArr;
}

语法有点混乱;它读作

       foo                                   -- foo
       foo(int width)                        -- is a function
                                             -- taking an int parameter
      *foo(int width)                        -- returning a pointer
     (*foo(int width))[HEIGHT]               -- to a HEIGHT-element array
char (*foo(int width))[HEIGHT]               -- of char

对于 C89,上述 sn-p 中的 HEIGHT 必须是编译时常量整数表达式(宏、数字文字或由宏和/或数字文字组成的算术表达式)。我不确定 C99 是否也是如此。

根据您发布的 sn-p,您要做的是获取您已经分配的数组并初始化其内容。请记住,在大多数情况下,数组类型的表达式将隐式转换为指向基类型的指针。 IOW,如果你将一个 T 的 N 元素数组传递给一个函数,该函数实际接收的是一个指向 T 的指针:

void foo (T *p) {...}
...
T arr[N];
foo(arr);

对于二维数组,它有点丑:

void foo (T (*p)[M]) {...}
...
T arr[N][M];
foo(arr);

这也依赖于在编译时已知的 M,这限制了函数的有用性。您想要的是一个可以处理任意大小的二维数组的函数。我知道实现这一点的最好方法不是传递指向数组的指针,而是传递数组 [1] 中第一个元素的地址,并将行数和列数作为单独的参数传递:

void foo(T *base, size_t rows, size_t cols) {...}
...
T arr[N][M];
foo (&arr[0][0], N, M);

所以你的 rand_grid 函数看起来像这样:

void rand_grid(char *base, size_t rows, size_t cols)
{
  size_t i, j;
  for (i = 0; i < rows; i++)
  {
    for (j = 0; j < cols; j++)
    {
      /**
       * Since base is a simple char *, we must index it
       * as though it points to a 1-d array.  This works if
       * base points to the first element of a 2-d array,
       * since multi-dimensional arrays are contiguous.  
       */
      base[i*cols+j] = initial_value();
    }
  }
}

int main(void)
{
  char A[WIDTH][HEIGHT];
  rand_grid(&A[0][0], WIDTH, HEIGHT);
  ...
}

  1. 尽管表达式&amp;A[0][0]A 产生相同的值(A 的基地址),但两个表达式的类型不同。第一个表达式计算为指向 char 的简单指针 (char *),而第二个表达式计算为指向 char 的二维数组的指针 (char (*)[HEIGHT])。

【讨论】:

  • 这个回复很有教育意义。谢谢!
  • 返回数组的 C++ 函数是否必须使用类似的方法? (略超出我所知道的问题范围,但差异不足以提出新问题)
【解决方案2】:

你不能。您可以将指向数组的指针作为参数传递并让函数对其进行修改,或者函数本身可以分配数据并返回指针。

在你的情况下

void rand_grid(char A[WIDTH][HEIGHT]) {
    A[0][0] = 'A'; // or whatever you intend to do
}

main() {
    char A[WIDTH][HEIGHT];
    rand_grid(A);
}

编辑:正如 caf 指出的那样,实际上可以返回带有数组的 struct,但当然没有一个头脑正常的 c 程序员会这样做。

【讨论】:

  • 您实际上可以通过将数组包装在struct 中来按值返回数组,但这是一个非常糟糕的主意。
  • 谢谢。我想知道如何解决以下问题:我需要一个填充随机整数的网格。我希望数组的初始化不在 main 函数上,因为它在程序的其余部分很有用。如何使用指针来解决问题?
  • Jaska,你应该把这个子问题作为一个单独的问题,即stackoverflow上的一个新问题。
  • 如果数组维度是在编译时设置的,您可以使用我回答中的代码 sn-p。它实际上是一个指针的传递。
  • 为什么在结构中返回数组是个坏主意?将小数组打包成结构并不少见。它可能比分配内存并返回指向它的指针更快,也更容易。
【解决方案3】:

您永远不能返回原始(值)类型以外的堆栈分配(“auto”)变量,以及此类structs。对于其他类型,您需要从堆中分配内存,使用malloc(),或者将(固定大小)数组包装成struct

如果您使用的是固定大小的数组,则可以将其建模为 struct 并使用 struct-return:

#define WIDTH  11
#define HEIGHT 11

typedef struct {
  unsigned char cell[WIDTH * HEIGHT];
} Board;

Board board_new(void)
{
  Board b;
  size_t i;

  for(i = 0; i < sizeof b.cell / sizeof *b.cell; i++)
    b.cell[i] = rand() & 255;
  return b;
}

这很好,并且不应该比使用显式指针的替代方法更昂贵:

void board_init(Board *b);

由于 struct-return 的前一种情况可以(由编译器)重写为后者。这称为return value optimization

【讨论】:

  • 这不是真的 - 你可以返回 struct 类型,它们绝对是非原始的。不过,这不是一个好主意。
  • 但是结构类型将从堆栈复制到调用者附近的(隐藏)局部变量中。如果将数组定义为固定大小,则它可以工作,以便编译器知道返回结构/数组的大小。但事实并非如此。这是一个动态数组...
  • malloc() 不是分配内存的唯一方法。您可以在函数中包含静态变量
  • Alex,不需要动态调整数组大小。
  • 放松,我反对你使用“最好”这个词,因为它不仅仅是不合适的;-)“最好的可能是将它建模为一个结构”。如果你改变措辞,我会收回我的反对票;-)
【解决方案4】:

如果你真的想这样做,你可以尝试将数组 A 设置为静态,这样 A 的存储不是由函数范围决定的,你实际上可以返回数组(当然以指针的形式)。

但这不是完成您想要实现的目标的好方法,而是将数组传递给函数 rand_grid 。这就是通过地址传递的意义。

【讨论】:

  • C 中没有“通过地址传递”之类的东西。我确定您的意思是 That's what passing the (value of the) array address is meant for.
【解决方案5】:

我知道从函数返回数组的所有方法都有弱点和优点。

包装在结构中避免了分配和释放内存的开销,也避免了记住释放。在使用 malloc、calloc 和 realloc 的任何解决方案中,您都会遇到这些问题。另一方面,包装在结构中需要知道数组的最大可能大小,并且对于大型数组来说无疑会浪费内存和执行时间(例如,将文件加载到内存中并在函数之间传递文件内容通过复制)。

【讨论】:

    猜你喜欢
    • 2021-05-30
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-04
    • 2012-04-01
    • 2018-05-15
    相关资源
    最近更新 更多