Функция для динамического выделения матрицы

Я хочу создать функцию для выделения (с malloc/calloc) матрицы, объявленной как двойной указатель. Я понял, как работает матрица двойных указателей и как распределить ее с помощью malloc, но когда я передаю свою матрицу (объявленную в main() и инициализированную до NULL), моя программа сработает. Я полагаю, что ошибка связана с моей функцией allocMatrix(), потому что, если я выделяю матрицу в main, все работает плавно. Спасибо: -)

Main:

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

#define ROW 5
#define COL 5

int main(void) {

    int i,j, ret;
    int nRow, nCol;
    int **mat=NULL; // double pointer matrix

    nRow = 0;
    nCol = 0;

    //Insert n of row and columns
    printf("Insert n of rows and columns:\n");
    scanf("%d %d", &nRow, &nCol);

    //Functions to allocate matrix
    ret=allocMatrix(mat, nRow, nCol);
    printf("Return value: %d\n",ret);

    /*
    this code works perfect!
    mat= malloc(nRow * sizeof(int));
    i=0;
    while( i < nRow)
    {
        mat[i]=malloc(nCol * sizeof(int));
        i++;
    }
    */

    //Get Values from stdin
    i=0;
    while( i < nRow)
    {
        j=0;
        while (j < nCol)
        {
            printf("Insert value pos[%d,%d]:\n", i, j);
            scanf("%d", &mat[i][j]);
            j++;
        }
        i++;
    }

    //Print values
    i=0;
    while (i < nRow)
    {
        j=0;
        while( j < nCol)
        {
            printf("Value pos[%d,%d] is: %d \n", i, j, mat[i][j]);
            j++;
        }
        i++;
    }


    system("pause");
    return EXIT_SUCCESS;
}

allocateMatrix:

int allocMatrix(int **matrix, int nRow, int nCol)
{
    int i;
    int ext_status;

    //Classic allocation method for matrix
    matrix= malloc( nRow * sizeof(int));

    if ( matrix != NULL)
    {
        i=0;
        while (i < nRow)
        {
            matrix[i]= malloc(nCol * sizeof(int));

            if( matrix[i] != NULL)
                ext_status= 1;
            else
                ext_status= 0;
            i++;
        }
    }
    else
        ext_status= 0;

    return ext_status;
 }

Ответ 1

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

Вместо этого правильно распределите 2D-массив в смежных ячейках памяти, например:

int x;
int y;

// store some values in x and y here

int(*matrix)[y] = malloc (sizeof(int[x][y]));

if(matrix == NULL)
{
  // error handling here
}

matrix[i][j] = something; // do something with the matrix

free(matrix);

Если вы настаиваете на сохранении этого кода в функции, это будет:

void* allocMatrix (int nRow, int nCol)
{
  return malloc (sizeof(int[nRow][nCol]));
}

int(*matrix)[y] = allocMatrix(x, y);

Изменить: объяснение указателей кода и массива.

В строке int(*matrix)[y] = malloc (sizeof(int[x][y]));, sizeof(int[x][y]) довольно прямолинейный, это просто размер 2D-массива int с размерами x * y. Он использует концепцию массивов переменной длины из стандарта C99, которая позволяет определять размеры массива во время выполнения.

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

Указатель массива записывается как type(*name)[size], поэтому, например, указатель на массив из 5 ints будет записан как int(*arr_ptr)[5] = &the_array;.

При обращении к указанному содержимому указатель массива ведет себя точно так же, как любой указатель, вы получаете доступ к его содержимому с помощью *. Поэтому *arr_ptr указывает массив, на который указывает, а (*arr_ptr)[0] - первый элемент этого массива.

Для многомерных массивов применяются те же правила. Учитывая массив int arr[x][y], указатель массива на этот тип будет int(*arr_ptr)[x][y] = &arr;. Доступ к содержимому *arr_ptr даст вам двумерный массив, который эквивалентен массиву массивов. (*arr_ptr)[0], следовательно, даст первый массив в массиве массивов. Обычное правило для любого имени массива при использовании в выражении состоит в том, что оно "распадается" на указатель на первый элемент. Здесь применяется то же самое, поэтому (*arr_ptr)[0] также будет таким же, как указатель на первый элемент в первом массиве. И (*arr_ptr)[0][0] предоставит первый элемент первого массива.

Теперь этот синтаксис (*arr_ptr)[0][0] выглядит немного трудным для чтения; для получения первого элемента 2D-массива мы используем при написании только arr[0][0]. Поэтому при объявлении указателя массива есть удобный трюк. Вместо объявления полного и правильного указателя массива: int(*matrix)[x][y], указатель массива на двумерный массив размеров x * y, мы вместо этого объявляем его как int(*matrix)[y], который является указателем массива на 1D-массив с размером y. Он будет указывать на первый элемент в 2D-массиве, который представляет собой 1D-массив размера y. Мы знаем, что 2D-массив содержит x таких элементов.

И из-за этого трюка мы теперь сможем использовать указатель массива с тем же синтаксисом, что и при доступе к 2D-массиву, а именно matrix[i][j], а не к трудночитаемому (*matrix)[i][j].

Ответ 2

Проблема заключается в том, что вы передаете mat самостоятельно функции распределения, которая не может удерживать выделенную память от функции при возврате, поскольку C использует значение pass-by-value для передачи аргумента функции.

Вам нужно передать указатель на mat и соответственно присвоить.

Ответ 3

Вы передаете матрицу в allocMatrix по значению, когда она должна быть по ссылке.

Определите свою функцию как int allocMatrix(int ***matrix, int nRow, int nCol) и замените matrix на *matrix (btw осторожно с синтаксисом индексации, он должен быть (*matrix)[i], а не *matrix[i]).

Это означает, что вы вызовете эту функцию как allocMatrix(&mat, nRow, nCol).

Ответ 4

где написано double, вы можете изменить для вашего типа матрицы

void allocMatrix(double ***matrix, int row, int col){
    int i = 0;
    *matrix = (double **)malloc(sizeof(double *) * row);
    for(i = 0; i < col; i++){
        *(*matrix + i) = (double *)malloc(sizeof(double) * col);
    }
}

void deallocMatrix(double **matrix, int row){
    int i = 0;
    for(i = 0; i < row; i++){
        free(matrix[i]);
    }
    free(matrix);
}

когда в основном вы можете использовать их, как это

int main(){
    int i = 0, j = 0, k = 0, row = 3, col = 4;
    double **myMatrix = NULL;
    allocMatrix(&myMatrix, row, col);
    for(i = 0; i < row; i++){
        for(j = 0; j < col; j++){
            myMatrix[i][j] = k++;
            printf("%.0lf\t", myMatrix[i][j]);
        }
        printf("\n");
    }
    deallocMatrix(myMatrix, row);
    return 0;
}

Выход

1    2    3    4
5    6    7    8
9    10   11   12

что может избежать утечек памяти, но, как сказал Lundin, лучше не использовать 2d-массивы

Никогда не используйте указатель на указатель для выделения многомерных массивов. Это широко распространенная, но плохая и неправильная практика. Это не даст вам истинный 2D-массив, и это приведет к более медленному коду из-за фрагментации кучи. Это также делает код сложнее писать, читать и поддерживать, что, в свою очередь, увеличивает потенциал утечки памяти. Вместо этого правильно распределите 2D-массив в смежных ячейках памяти

[]