Возврат VLA и использование

У меня есть следующая функция:

int* create_matrix_2(int rows, int cols)
{
    double (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            *A[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            printf("%lf, " , *A[row][col]);
        }
        printf("\n");
    }
    return A;
}

Мой вопрос: как мне вернуть VLA из этой функции, каков тип и как мне записать это как сигнатуру функции, и как я могу использовать ее там, где я получаю возвращенный массив?

На данный момент я пытаюсь сделать это так:

int (*matrix2)[height][width] = create_matrix_2(height, width);

for (int row = 0; row < height; row++)
{
    for (int col = 0; col < width; col++)
    {
        printf("%d" , (*matrix2)[row][col]);
    }
    printf("\n");
}

а затем запустить: gcc 2d_array_test.c -o 2d_array_test.out -std=c99 -O0

Но это приводит к следующим проблемам:

2d_array_test.c: In function ‘main’:
2d_array_test.c:35:34: warning: initialization from incompatible pointer type [enabled by default]
  int (*matrix2)[height][width] = create_matrix_2(height, width);
                                  ^
2d_array_test.c: In function ‘create_matrix_2’:
2d_array_test.c:105:2: warning: return from incompatible pointer type [enabled by default]
  return A;
  ^

EDIT # 1:

Я попытался использовать код, предложенный alk, но он дает мне много ошибок при компиляции. Вот отдельная программа, которая содержит предложенный вами код с основной функцией: http://pastebin.com/R6hKgvM0 Я получаю следующие ошибки:

2d_array_test_new.c: In function ‘main’:
2d_array_test_new.c:18:2: warning: passing argument 3 of ‘create_matrix’ from incompatible pointer type [enabled by default]
  if (-1 == create_matrix(height, width, &matrix))
  ^
2d_array_test_new.c:10:5: note: expected ‘int **’ but argument is of type ‘int (**)[(sizetype)(height)][(sizetype)(width)]’
 int create_matrix(size_t, size_t, int**);
     ^
2d_array_test_new.c: At top level:
2d_array_test_new.c:37:5: error: conflicting types for ‘create_matrix’
 int create_matrix(size_t rows, size_t cols, int(**a)[rows][cols])
     ^
2d_array_test_new.c:10:5: note: previous declaration of ‘create_matrix’ was here
 int create_matrix(size_t, size_t, int**);
     ^
2d_array_test_new.c: In function ‘create_matrix’:
2d_array_test_new.c:45:11: error: ‘EINVAL’ undeclared (first use in this function)
   errno = EINVAL;
           ^
2d_array_test_new.c:45:11: note: each undeclared identifier is reported only once for each function it appears in
2d_array_test_new.c:40:6: warning: variable ‘errno’ set but not used [-Wunused-but-set-variable]
  int errno;
      ^

Похоже, что ошибки в основном связаны с типом возврата. Как правильно написать тип этого массива?

4 ответа

Решение
  • ссылаясь на 1- е предупреждение:

    warning: initialization from incompatible pointer type
    

    Вот

    int (*matrix2)[height][width] = create_matrix_2(height, width);
    

    int (*matrix2)[height][width] а также int * просто не то же самое.

  • ссылаясь на второе предупреждение:

    warning: return from incompatible pointer type 
    

    Это связано с определением

    double (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));
    

    возвращаясь A от int * create_matrix_2(),

    int * а также double (*A)[rows][cols] также не то же самое.

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

#include <errno.h> /* for errno and EINVAL */
#include <stdlib.h> /* for malloc() */


int create_matrix_2(size_t rows, size_t cols, int(**a)[rows][cols])
{
  int result = 0;

  if (NULL == a)
  {
    result = -1;
    errno = EINVAL;
  }
  else
  {
    (*a) = malloc(sizeof **a);
    if (NULL == (*a))
    {
      result = -1;
    }
    else
    {
      for (size_t row = 0; row < rows; row++)
      {
        for (size_t col = 0; col < cols; col++)
        {
          (**a)[row][col] = row * cols + col;
        }
      }

      for (size_t row = 0; row < rows; row++)
      {
        for (size_t col = 0; col < cols; col++)
        {
          printf("%d, " , (**a)[row][col]);
        }

        printf("\n");
      }
    }
  }

  return result;
}

и назовите это так:

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


int create_matrix_2(size_t rows, size_t cols, int(**a)[rows][cols]);


int main(void)
{
  int result = EXIT_SUCCESS;

  int (*matrix2)[height][width] = NULL;
  if (-1 == create_matrix_2(height, width, &matrix2))
  {
    perror("create_matrix_2() failed");
    result = EXIT_FAILURE;
  }
  else
  {
    for (size_t row = 0; row < height; row++)
    {
      for (size_t col = 0; col < width; col++)
      {
        printf("%d, " , (*matrix2)[row][col]);
      }

      printf("\n");
    }

    free(matrix2);
  }

  return result;
}

Во-первых, есть некоторые проблемы с вашим кодом. У вас есть двойной указатель массива, указывающий на двумерный массив целых, что не имеет никакого смысла.

Кроме того, так как A указатель на 2D массив, вы не можете сделать *A[row][col], Поскольку [] имеет более высокий приоритет, чем *, поэтому код эквивалентен *(A[row][col]), Что означает "дай мне" row количество двумерных матриц, то... ". Код будет в конечном итоге далеко за пределами.

Хитрость при объявлении указателя массива на динамически размещаемый многомерный массив состоит в том, чтобы опустить самое внутреннее измерение. Таким образом, вы можете использовать синтаксис, как если бы вы использовали обычный многомерный массив.

double (*A)[cols] = malloc(sizeof(double[rows][cols]));
...
A[x][y] = ...;

A[x][y] будет означать "В моем массиве A, где каждый элемент является массивом размера cols, получить доступ к номеру массива x, а затем индекс y в этом массиве".


Что касается вопроса о том, как вернуть массив этого типа... ну, вы не можете, потому что объявление функции должно содержать измерения указателя массива. Это должно быть чудовищно, как это:

double (*create_matrix_2(int rows, int cols))[rows][cols]   // wont compile

Если мы проигнорируем, что синтаксис C для возврата указателей массива (и указателей на функции) полностью FUBAR, вышеупомянутое недопустимо C. Поскольку строки и столбцы должны были бы быть известны во время компиляции.

Решение для устранения вышеуказанного беспорядка и одновременного исправления проблемы заключается в возврате указателя массива через параметр:

void create_matrix_2 (int rows, int cols, double(**array)[rows][cols])
{
    // omit inner-most dimension to get sane syntax:
    double (*A)[cols] = malloc(sizeof(double[rows][cols]));

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            A[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            printf("%lf, " , A[row][col]);
        }
        printf("\n");
    }

    // "brute" pointer conversion but doesn't break aliasing:
    *array = (void*)A;
}

Короткий ответ: ты не можешь.

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

Однако вы можете изменить свое create_matrix2() вернуть пустой указатель. При этом используется тот факт, что в C (почти) любой указатель может быть неявно преобразован в void указатель и наоборот.

   void *create_matrix2(int rows, int columns)
   {
       /* as you've implemented it, except that A should be int not double */
   }

   int main()
   {
        int rows = 2, cols = 3;
        int (*m)[rows][cols] = create_matrix2(rows, cols);

         /*  use *m as a pointer to an array with 2 rows and 3 cols */
   }

Затем он создает иллюзию того, что вы ищете.

Опасность этого - и причина, по которой я сейчас использую слово "иллюзия" - в том, что обращение в и из void Указатель не позволяет компилятору выполнять проверку типов. Так что это пройдет мимо компилятора, но вызовет неопределенное поведение из-за падения конца массива.

   int main()
   {
        int rows = 2, cols = 3;
        int (*m)[rows][cols] = create_matrix2(rows-1, cols-1);   /*  whoops - deliberate typos here */

         /*  use *m as a pointer to an array with 2 rows and 3 cols */
   }

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

Образец

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

void * create_matrix_2(int rows, int cols){
    int (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));

    for (int row = 0; row < rows; row++){
        for (int col = 0; col < cols; col++){
            (*A)[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++){
        for (int col = 0; col < cols; col++){
            printf("%d, " , (*A)[row][col]);
        }
        printf("\n");
    }
    return A;
}

int main(void){
    int width = 5, height = 3;
    int (*matrix2)[height][width] = create_matrix_2(height, width);

    for (int row = 0; row < height; row++){
        for (int col = 0; col < width; col++){
            printf("%d " , (*matrix2)[row][col]);
        }
        printf("\n");
    }
    free(matrix2);
    return 0;
}
Другие вопросы по тегам