Объявление функции C для возврата массива

Как я могу создать функцию, которая возвращает массив? Я попробовал это

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

Ответ 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, HEIGHT в приведенном выше фрагменте должен быть постоянным интегральным выражением времени компиляции (либо макросом, числовым литералом, либо арифметическим выражением, состоящим из макросов и/или числовых литералов). Я не уверен, что это также верно для C99.

На основе фрагмента, который вы опубликовали, вы хотите сделать массив, который вы уже выделили, и инициализировать его содержимое. Помните, что в большинстве контекстов выражение типа массива будет неявно преобразовано в указатель на базовый тип. IOW, если вы передаете N-элементный массив из T в функцию, то, что фактически получает функция, является указателем на T:

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

Для 2-мерных массивов это немного уродливее:

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

Это также зависит от того, что M известно во время компиляции, что ограничивает полезность функции. То, что вам нужно, это функция, которая может работать с 2-мерным массивом произвольного размера. Лучший способ, которым я это знаю, - это не передавать указатель на массив, передавать адрес первого элемента в массиве [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);
  ...
}

  • Даже если выражения &A[0][0] и A дают одно и то же значение (базовый адрес A), типы этих двух выражений различны. Первое выражение оценивает простой указатель на char (char *), а второй - на указатель на 2-й массив char (char (*)[HEIGHT]).

Ответ 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);
}

Изменить. Как указано в приложении, можно фактически вернуть struct с массивом в нем, но, конечно же, c-программист в здравом уме не сделает этого.

Ответ 3

Вы никогда не сможете вернуть выделенную стекю ( "auto" ) переменную чего-то другого, кроме типа примитива (value) и struct. Для других типов вам нужно выделить память из кучи, используя 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 может быть переписан (компилятором) последнему. Это называется оптимизация возвращаемого значения.

Ответ 4

Если вы действительно хотите сделать это, вы можете попытаться сделать массив A статическим, таким образом, хранилище для A не определяется областью действия функции, и вы можете фактически вернуть массив (в виде указателя, конечно).

Но это не лучший способ выполнить то, что вы пытаетесь достичь, вместо этого передайте массив функции rand_grid. То, что означает пропуск через адрес.

Ответ 5

Все методы, о которых я знаю, возвращают массив из функции, имеют слабые стороны и сильные стороны.

Обтекание в структуре избавляет от накладных расходов на выделение и освобождение памяти, а также позволяет избежать запоминания. У вас есть проблемы с любым решением, которое использует malloc, calloc и realloc. С другой стороны, обертка в структуре требует знания максимально возможного размера массива и, безусловно, расточительна память и время выполнения для больших массивов (например, загрузка файла в память и передача содержимого файла из функции в функцию путем копирования).