Возвращает 2D массив заданного пользователем размера

#c #function #pointers #multidimensional-array

Вопрос:

Я хочу создать функцию, которая

  1. принимает строку и столбец в качестве аргументов.
  2. принимает входные данные в соответствии с заданным размером и
  3. возвращает 2D матрицу/массив.

Я прошел через множество решений в Интернете, но все, что я пытаюсь, дает мне некоторые новые ошибки.

 int* input_taker(int row, int col)
{
    int mat[row][col];
    for (int i = 0; i < row; i  )
    {
        for (int j = 0; j < col; j  )
        {
            scanf("%d", amp;mat[i][j]);
        }
    }
    return mat;
}
 

Комментарии:

1. почему бы не std::вектор?

2. Какие ошибки вы получаете? Я получаю только предупреждения, и программа успешно компилируется.

3. C или C ? В любом случае, ваш код является незаконным в обоих

4. @4386427 почему незаконно?

5. @WeatherVane Мы не расходимся во мнениях…

Ответ №1:

  1. Тип возвращаемой функции таков int* , но вы пытаетесь вернуть mat функцию, которая имеет другой тип (т. Е. int* (*)[col] ), так что это ошибка.
  2. mat является локальной переменной функции, поэтому она не существует после возврата функции. Другими словами — возвращаться mat вообще не имеет смысла. Вы возвращаете ссылку на «мертвый» объект.

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

Для этого вы можете определить mat как указатель на массив col целых чисел. Кроме того, функция должна возвращать указатель на массив целых чисел.

Нравится:

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

int (* input_taker(int row, int col))[]
{
    int (*mat)[col] = malloc(row * sizeof *mat);
    if (mat == NULL) exit(1);
    for (int i = 0; i < row; i  )
    {
        for (int j = 0; j < col; j  )
        {
            mat[i][j] = i*col   j;  // Here you can read user input
        }
    }
    return mat;
}

int main ()
{
    int col = 8;
    int row = 5;
    int (* p)[col] = input_taker(row, col);
    for (int i=0; i<row;   i)
    {
        for (int j=0; j<col;   j)
        {
            printf("] ", p[i][j]);
        }
        puts("");
    }
    free(p);
    return 0;
}
 

Выход

     0     1     2     3     4     5     6     7 
    8     9    10    11    12    13    14    15 
   16    17    18    19    20    21    22    23 
   24    25    26    27    28    29    30    31 
   32    33    34    35    36    37    38    39 
 

Комментарии:

1. @PaulOgilvie Не стесняйтесь публиковать «намного более простое, эквивалентное решение» в качестве ответа. Это поможет ОП и другим. Я не знаю лучшего способа, чем использовать «указатель на массив» при работе с постоянным количеством столбцов.

2. @PaulOgilvie: В этом ответе нет ничего сверхъестественного. Это обеспечивает простой и четко определенный способ создания двумерного массива.

3. ОК. Я отозвал свой комментарий.

4. @4386427 Это было немного трудно понять такому новичку, как я. Но это решило мою проблему. Спасибо за это.

5. @SheikhAbdullah Изучите разницу между int *p[] ними и int (*p)[] см., например, cdecl.org

Ответ №2:

В других ответах использовался ВЛАс, как и в вашем первоначальном вопросе. Дело в том, что я не думаю, что VLAS подходят для матриц, поскольку маловероятно, что во время выполнения они будут иметь одинаковый размер изменения переменной, а для этого требуется мутный дизайн с VLAs.

Если вы откажетесь от синтаксиса двойных квадратных скобок, использование простого указателя будет довольно прямым (для этого я адаптирую код 4386427).:

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

int *input_taker(int row, int col)
{
    int *mat = malloc(row * col * sizeof *mat);
    for (int i = 0; i < row; i  ) {
        for (int j = 0; j < col; j  ) {
            mat[i * col   j] = i * col   j;  // Here you can read user input
        }
    }
    return mat;
}

int main ()
{
    int col = 8;
    int row = 5;
    int *p = input_taker(row, col);
    for (int i = 0; i < row;   i) {
        for (int j = 0; j < col;   j) {
            printf("] ", p[i * col   j]);
        }
        puts("");
    }
    free(p);
    return 0;
}
 

Еще лучше упаковать все в структуру:

 struct mat {
    int rows, cols;
    int *data;
};
 

и предоставьте функции для его использования.

Комментарии:

1. Это не 2D массив, а 1D массив, который приводит к индексированию, подверженному ошибкам

2. «для этого требуется мрачный дизайн с VLAs». Нет, это просто изменить количество строк. Изменение количества cols одинаково сложно с вашим решением

3. @4386427 Доступ через структуру должен решить эту проблему.

4. Зачем усложнять простые вещи?

5. @4386427 Хороший вопрос. Я предполагаю, что комментарий Пола Огилви к вашему ответу показывает, насколько «сложно» не всегда согласовано.

Ответ №3:

2D массив заданного пользователем размера

и

указатель на массив col целых чисел.

оба, похоже, избегают этого термина VLA . В ответе 4386 содержится предупреждение «C90 запрещает VLA» -Wvla .

Этот

int mat[row][col];

доза не работает, потому что это автоматическое хранение.

Но

int (*mat)[col];

это всего лишь указатель на VLA; его можно переместить и вернуть.

Для (чрезмерного)упрощения типа функции 4386 и разделения определения mat этого можно сделать:

 void *array_maker(int row, int col)   // just a pointer; no dimensions, no type
{
    int (*mat)[col];                  // declare runtime inner dim.: ptr to VLA  
    mat = malloc(row * sizeof *mat);  // mallocate both dims

    for (int i = 0; i < row; i  )
    for (int j = 0; j < col; j  )
            mat[i][j] = i*col   j;    // fill the array[][] 
    return mat;
}
 

Звонок из main-это:

 int col, row;

int (*p)[col=8];                  // ptr. to VLA  
p = array_maker(row=5, col);      // implicit cast from void-ptr 
 

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

 void array_filler(int row, int col, int mat[][col])
{
    for (int i = 0; i < row; i  )
    for (int j = 0; j < col; j  )
            mat[i][j] = i*col   j;    // fill the array[][] 
}
 

Теперь массив должен быть выделен вызывающим — либо в виде автоматического VLA, либо в виде указателя VLA, либо в виде массива фиксированного размера:

 col=row=9;
int mat[row][col];  
//int (*mat)[col] = malloc(row * sizeof*mat); 
//int mat[9][9];
array_filler(row, col, mat);
 

int mat[row][col]; неправильная продолжительность хранения

int* input_taker(int row, int col) -> предупреждение о несовместимом типе

Ответ №4:

Ваша проблема, похоже, заключается в болтающемся указателе. Вы должны выделить память перед вызовом input_taker.

 #include <cstdio>
#include <cstdlib>

int* input_taker(int *ptr, int row, int col)
{
    for (int i = 0; i < row; i  )
    {
        int *arr = amp;ptr[i*col];
        for (int j = 0; j < col; j  )
        {
            scanf("%d", amp;arr[j]);
        }
    }
    return ptr;
}

int main() {
    int n_rows, n_cols;
    n_rows = 2;
    n_cols = 3;
//    int *matrix = (int*)malloc(n_rows*n_cols*sizeof(int));
    int matrix [n_rows][n_cols];
    input_taker(amp;matrix[0][0], n_rows, n_cols);

    for (int i = 0; i < n_rows; i  )
    {
        printf("[");
        for (int j = 0; j < n_cols; j  )
        {
            j==n_cols-1?printf("%d]n", matrix[i][j]):printf("%d, ", matrix[i][j]);
        }
    }
    return 0;
}
 

Я изменил параметры функции, но сохранил возвращаемое значение. Однако вы можете вернуть его недействительным.