Возврат 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;
}